人生是一场不能存盘的RPG,我只能尽量多搞几个Screenshot

October 12, 2006

把势与pattern

把势与pattern
-AlonePlayer@gmail.com 2006.10.12
把式或把势,武术的旧称,实指武术中的架势.谚曰:”练拳不练把,等于瞎胡打;宁教十趟拳,不教一个把”
武术中的每一势都是实战中萃取出的精华(不包含那些舞蹈化,艺术化的东西),简单者,一看即知用法,小孩
打架或许都曾用过,高妙者内藏玄机,可以千变万化,也可以不变应万变,要理解需要知道它的各种应用场景,
发招的劲道火候.先贤为了武术的传承,把势排列组合,形成套路,同时也暗示了某些势的组合应用,但这也带
来一些负面影响,一者使人的思维拘泥与套路,难以随心而发,再者练习时平均分配时间,不利于重点练习.大师
在技臻化境时往往忘记了套路,体内产生一种本能,见招即有应对,且对时间,空间,力量,速度拿捏
精准,接应之妙,令人叹为观止.而对这些临敌因素,则千变万化,无法见诸文字,可谓一说即错,后学者务必耐心
揣摩,勤于练习,多多实践,方能有所得.

July 26, 2006

ADO.NET 中的抽象工厂

Filed under: Design Pattern

用Interface或抽象类来生成一组相关的对象.
以ADO.Net2.0为例
见(Abstract Factory Design Pattern in ADO.NET 2.0 by Muhammad Mosa)
ado.net 定义了一组抽象的class: DbConnection, DbCommand, DbParameter等,
每个抽象class都有几个对应的实现: SqlConnection, OralceConnection.

ado.net 2.0中的DbProviderFactory class充当了abstract factory的角色,
DbProviderFactory中定义了一系列careateXXX函数来生成connection, command等class.
从它派生的SqlClientFactory, OracleClientFactory 等重载了这些createXXX方法,
可以生成具体的connection, command.

优点在于
1. 隔离具体的class
2. 可以任意替换一系列的class
3. 强制使用统一系列的class

缺点:
AbstractFactory interface 定义了需要产生的class的规格,如果需要增加一个新的系列,
需要从头到尾搞一套Factory class.

如果AbstractFactory 定义一个CreateProduct()方法,使用.net的reflection来生成对象,就
可以省略Concrate Factory的定义.

July 25, 2006

读Martin Fowler的Inversion of Control Containers and the Dependency Injection pattern

Filed under: Design Pattern

出处: http://www.martinfowler.com/articles/injection.html

—by Riven Huang 2007.04.25

假设有这样一个case, 需要列出某个导演的作品, 为此需要以下的class:

namespace RivenStudy
{
public class MovieLister
{
private IMovieFinder finder;

public Movie[] MoviesDirectedBy(String arg)
{
List<Movie> allMovies = finder.FindAll();
foreach(Movie movie in allMovies )
{
if (!movie.Director.Equals(arg))
allMovies.Remove(movie);
}
return (Movie[])allMovies.ToArray();

}
}
}
为了让MoiveLister不关系数据的存储方式, 引入了一个finder Object,
MovieLister会依赖于finder Object的FindAll() 方法,为此我们定义了
如下的接口:

public interface IMovieFinder
{
List<Movie> FindAll();
}

在真正的操作中,MovieLister会用到一个具体的MovieFinder比如ColonDelimitedMovieFinder,
此movie finder会从一个text文件中读取影片的列表.

在这种情况下,我们的代码会写做:

public class MovieLister
{
private IMovieFinder finder;

public MovieLister()
{
finder = new ColonDelimitedMovieFinder(”movies1.txt”);
}
public Movie[] MoviesDirectedBy(String arg)
{
List<Movie> allMovies = finder.FindAll();
foreach(Movie movie in allMovies )
{
if (!movie.Director.Equals(arg))
allMovies.Remove(movie);
}
return (Movie[])allMovies.ToArray();

}
}

我们希望MovieLister 类能够与MovieFinder的 任何 实现类协同工作,并且允许在运行期插入具体的实现类,
插入动作完全脱离原作者的控制。在Patterns of Enterprise Application Architecture
(http://www.martinfowler.com/books.html#eaa)一书中,作者称此模式为PlugIn

而此刻设计中,MovieLister 类它直接实例化IMovieFinder的具体类。这样一来,
MovieLister既依赖于IMovieFinder接口,也依赖于实现IMovieFinderde类。
IMovieFinder 也就不成其为一个插件了,因为它并不是在运行期插入应用程序中的。

通过Interface对用到的组件加以抽象,并通过Interface与具体的组件通信,(如果组件并没有设计一个接口,
也可以通过适配器与之交流),同时我们希望以PlugIn的方式以多种方式部署这个系统,所以,现在的核心问题就是:
如何将这些插件组合成一个应用程序?
这正是(lightweight containers)轻量级容器所面临的问题,解决这个问题的手段就是是控制反转(Inversion of Control)

在前面的例子中,需要翻转(Inversion)的是应用程序对插件的依赖,达到这个目的,可以使用
Dependency Injection 模式并不是唯一的选择,你也可以用ServiceLocator 模式获得同样的效果.

Dependency Injection 模式的基本思想是:
用一个单独的对象(assembler)来获得IMovieFinder的一个具体实现,并将其实例赋给MovieLister的一个字段。

Dependency Injection的三种形式:
1. Constructor Injection
2. Setter Injection
3. Interface Injection

依赖注入的最大好处在于:它消除了MovieLister类对具体MovieFinder实现类的依赖。把MovieLister 类交给辅助类,
让他们根据实际环境插入(构造)一个合适的IMovieFinder实现

* 使用PicoContainer (http://www.picocontainer.org/)进行构造子注入

PicoContainer 通过一个构造器来判断如何将IMovieFinder的实例注入MovieLister:

class MovieLister…
{
public MovieLister(IMovieFinder finder)
{
this.finder = finder;
}
}

IMovieFinder 实例也由PicoContainer来管理
class ColonMovieFinder
{
private string fileName;
public ColonMovieFinder(String fileName)
{
this.fileName = fileName;
}
}

随后,在一个单独的class中配置PicoContainer,说明各个接口分别与哪个实现类关联,将哪个字符串注入MovieFinder组件。

private MutablePicoContainer configureContainer()
{
MutablePicoContainer pico = new DefaultPicoContainer();
Parameter[] finderParams = {new ConstantParameter(”movies1.txt”)};
pico.registerComponentImplementation(MovieFinder.class, ColonMovieFinder.class, finderParams);
pico.registerComponentImplementation(MovieLister.class);
return pico;
}

* 使用Spring 进行设值方法注入
为了让MovieLister 类接受注入, 需要为它定义一个set方法,
class MovieLister
{
private MovieFinder finder;
public void setFinder(MovieFinder finder)
{
this.finder = finder;
}
}

在MovieFinder的实现类中,也定义了一个set方法

class ColonMovieFinder
{
public void setFilename(String filename)
{
this.filename = filename;
}
}

然后设定Spring的配置文件
<beans>
<bean id=”MovieLister” class=”spring.MovieLister”>
<property name=”finder”>
<ref local=”MovieFinder”/>
</property>
</bean>

<bean id=”MovieFinder” class=”spring.ColonMovieFinder”>
<property name=”filename”>
<value>movies1.txt</value>
</property>
</bean>
</beans>

使用如下:
public void TestWithSpring()
{
ApplicationContext ctx = new FileSystemXmlApplicationContext(”spring.xml”);
MovieLister lister = (MovieLister) ctx.getBean(”MovieLister”);
Movie[] movies = lister.moviesDirectedBy(”Sergio Leone”);
assertEquals(”Once Upon a Time in the West”, movies[0].getTitle());
}

* 接口注入
1 . 定义一个接口,这个接口的用途是将 一个IMovieFinder实例注入该接口的实现者。
public interface InjectFinder
{
void injectFinder(MovieFinder finder);
}

这个接口应该由提供IMovieFinder 接口的人提供。任何想要使用IMovieFinder 实例的类
如MovieLister 类,都必须实现这个接口。

class MovieLister : InjectFinder
{
public void injectFinder(MovieFinder finder) {
this.finder = finder;
}

然后,我使用类似的方法将文件名注入MovieFinder的实现类:
public interface InjectFilename
{
void injectFilename (String filename);
}

class ColonMovieFinder : IMovieFinder, InjectFilename
{
public void injectFilename(String filename) {
this.filename = filename;
}

通过一些配置代码装配所有的组件并使用。
class IfaceTester
{
private MovieLister lister;
private void configureLister()
{
ColonMovieFinder finder = new ColonMovieFinder();
finder.injectFilename(”movies1.txt”);
lister = new MovieLister();
lister.injectFinder(finder);
}
测试代码则可以直接使用这个字段:
public void TestIface()
{
configureLister();
Movie[] movies = lister.moviesDirectedBy(”Sergio Leone”);
assertEquals(”Once Upon a Time in the West”, movies[0].getTitle());

}
}

使用Service Locator来进行依赖注入:
Service Locator 的基本思想是:有一个对象(即Service Locator)知道如何获得一个应用
程序所需的所有服务。即在上面的例子中,服务定位器应该有一个方法用于获得一个IMovieFinder的实例。

class MovieLister
{

IMovieFinder finder = ServiceLocator.movieFinder();
}

class ServiceLocator
{
private static ServiceLocator soleInstance;
private MovieFinder movieFinder;

public static IMovieFinder movieFinder()
{
return soleInstance.movieFinder;
}

public static void load(ServiceLocator arg)
{
soleInstance = arg;
}

public ServiceLocator(IMovieFinder movieFinder)
{
this.movieFinder = movieFinder;
}
}

使用
class Tester
{
private void configure()
{
ServiceLocator.load(new ServiceLocator(new ColonMovieFinder(”movies1.txt”)));
}

public void testSimple()
{
configure();
MovieLister lister = new MovieLister();
Movie[] movies = lister.moviesDirectedBy(”Sergio Leone”);
assertEquals(”Once Upon a Time in the West”,
movies[0].getTitle());
}
}

Dependency Injection 和Service Locator 并不互斥,你可以同时使用它们,
一个简单的Avalon 实现版本:
public class MyMovieLister implements MovieLister, Serviceable
{
private MovieFinder finder;
public void service( ServiceManager manager )
{
finder = (MovieFinder)manager.lookup(”finder”);
}
}

service() 方法就是接口注入的例子, 它使容器可以将一个ServiceManager 对象注入
MyMovieLister 对象。
ServiceManager则是一个服务定位器。MyMovieLister 并不把ServiceManager 对象保存在字段中,
而是马上借助它找到IMovieFinder 实例,并将后者保存起来。

May 28, 2006

设计模式纵横谈(15)Command 命令模式

Filed under: Design Pattern

隔离 行为请求者 和 行为实现者 之间的耦合.
将行为抽象成object.
行为的请求者传递一个对象,该对象表达了一个方法(行为)

//== Version 1.
class Application
{
    public void Show()
    {
        Document doc = new Document();
        doc.ShowText();
       
        Graphics graph = new Graphics();
        graph.ShowGraphics();
    }
       
}

class Document
{
    public void ShowText()
    {
        …
    }
}

class Graphics
{
    public ShowGraphics()
    {
        …
    }
}

此时,Application 直接依赖于 document 和 graphics的showXX()这样的
具体行为实现.
考虑如何实现undo,redo,需要添加大量的代码.

//== Version 2.
public interface Command
{
    public void Show();
    public void Undo();
    public void Redo();
}

//==这个设计违背了单一职责原理,
class Document : Command
{
    public virtual void Show()
    {
   
    }
}

class Graphics : Command
{
    public virtual void Show()
    {
   
    }
}

class Application
{
    Stack<Command> commands;
    Stack<Command> undoList;
   
    public void Show()
    {
        foreach(Command c in list)
        {
            c.Show();
        }
    }
   
    public void Undo()
    {
        if(canUndo)
        {
            Command command = commands.Pop();
            command.Undo();
            undoList.Push(command);
        }
    }
   
    public void Redo()
    {
        if(canUndo)
        {
            Command command = undoList.Pop();
            command.Redo();
        }
    }       
}

//== Version 3.  扩展已有的类
//已存在,实现细节
class Document : Command
{
    public virtual void Show()
    {
   
    }
}

class Graphics : Command
{
    public virtual void Show()
    {
   
    }
}

//==实现command 模式
public interface Command
{
    public void Show();
    public void Undo();
    public void Redo();
}

//==具体化的行为对象,
class DocumentCommand : Command
{
    Document doc;
   
    public DocumentCommand(Document doc)
    {
        this.doc = doc;
    }
   
    public void Show()
    {
        doc.ShowText()
    }
   
    public void Undo()
    {
        …
    }
   
    public void Redo()
    {
        …
    }
}

此时,Application不再依赖于document和graphics这样的细节, 而是依赖于Command这样的
抽象,体现了依赖导致原则.

Command和Delegate之间的选择:

Command更趋向于oo的特征,更符合抽象的原则. Delegate 更灵活






















Get free blog up and running in minutes with Blogsome
Theme designed by Hadley Wickham