(转)在 Java 中应用设计模式 - Factory Method由此可以清楚的看出这样的平行对应关系: Product Cre
(转)在 Java 中应用设计模式 - Factory Method
由此可以清楚的看出这样的平行对应关系: Product <====> Creator ; ConreteProduct <====> ConreteCreator
抽象产品对应抽象创建器,具体产品对应具体创建器.这样做的好处是什么呢?为什么我们不直接用具体的产品和具体的创建器完成需求呢?实际上我们也可以这样做.但通过FactoryMethod模式来完成,客户(client)只需引用抽象的Product和Creater,对具体的ConcreteProduct和ConcreteCreator可以毫不关心,这样做我们可以获得额外的好处:
首先客户端可以统一从抽象创建器获取产生的实例,Creator的作用将client和产品创建过程分离开来,客户不用操心返回的是那一个具体的产品,也不用关心这些产品是如何创建的.同时,ConcreteProduct也被隐藏在Product后面,ConreteProduct继承了Product的所有属性,并实现了Product中定义的抽象方法,按照Java中的对象造型(cast)原则,通过ConcreteCreator产生的ConcreteProduct可以自动的上溯造型成Product.这样一来,实质内容不同的ConcreteProduct就可以在形式上统一为Product,通过Creator提供给client来访问. 其次,当我们添加一个新的ConcreteCreator时,由于Creator所提供的接口不变,客户端程序不会有丝毫的改动,不会带来动一发而牵全身的灾难, 这就是良好封装性的体现.但如果直接用ConcreteProduct和ConcreteCreator两个类是无论如何也做不到这点的.优良的面向对象设计鼓励使用封装(encapsulation)和委托(delegation),而FactoryMethod模式就是使用了封装和委托的典型例子,这里封装是通过抽象创建器Creator来体现的,而委托则是通过抽象创建器把创建对象的责任完全交给具体创建器ConcreteCreator来体现的.
现在,请再回头看看基本概念中的那段话,开始也许觉得生涩难懂,现在是不是已经明朗化了很多.
下面让我们看看在 Java 中如何实现Factory Method模式,进一步加深对它的认识.
具体实施
先说明一点,用FactoryMethod模式创建对象并不一定会让我们的代码更短,实事上往往更长,我们也使用了更多的类,真正的目的在于这样可以灵活的,有弹性的创建不确定的对象.而且,代码的可重用性提高了,客户端的应用简化了,客户程序的代码会大大减少,变的更具可读性.
标准实现: 这里我采用Bruce Eckel 用来描述OO思想的经典例子Shape.这样大家会比较熟悉一些.我完全按照图1中所定义的结构写了下面的一段演示代码.这段代码的作用是创建不同的Shape实例,每个实例完成两个操作:draw和erase.具体的创建过程委托?oShapeFactory来完成.1.a 首先定义一个抽象类Shape,定义两个抽象的方法.
abstract?class?Shape?{
??//?勾画shape
??public?abstract?void?draw();
??//?擦去?shape
??public?abstract?void?erase();
??public?String?name;
??public?Shape(String?aName){
????name?=?aName;
??}
}1.b 定义 Shape的两个子类: Circle, Square,实现Shape中定义的抽象方法
//?圆形子类
class?Circle?extends?Shape?{
??public?void?draw()?{
????System.out.println("It?will?draw?a?circle.");
??}
??public?void?erase()?{
????System.out.println("It?will?erase?a?circle.");?
??}
??//?构造函数
??public?Circle(String?aName){
????super(aName);
??}
}
//?方形子类
class?Square?extends?Shape?{
??public?void?draw()?{?
????System.out.println("It?will?draw?a?square.");?
??}
??public?void?erase()?{?
????System.out.println("It?will?erase?a?square.");?
??}
??//?构造函数
??public?Square(String?aName){
????super(aName);
??}
}1.c 定义抽象的创建器,anOperation调用factoryMethod创建一个对象,并对该对象进行一系列操作.
abstract?class?ShapeFactory?{??
??protected?abstract?Shape?factoryMethod(String?aName);
??//?在anOperation中定义Shape的一系列行为
public?void?anOperation(String?aName){
????Shape?s?=?factoryMethod(aName);
????System.out.println("The?current?shape?is:?"?+?s.name);
????s.draw();
????s.erase();
??}
}1.d 定义与circle和square相对应的两个具体创建器CircleFactory,SquareFactory,实现父类的methodFactory方法
//?定义返回?circle?实例的?CircleFactory
class?CircleFactory?extends?ShapeFactory?{
??//?重载factoryMethod方法,返回Circle对象
??protected?Shape?factoryMethod(String?aName)?{
????return?new?Circle(aName?+?"?(created?by?CircleFactory)");
??}
}
??
//?定义返回?Square?实例的?SquareFactory
class?SquareFactory?extends?ShapeFactory?{
??//?重载factoryMethod方法,返回Square对象
protected?Shape?factoryMethod(String?aName)?{
????return?new?Square(aName?+?"?(created?by?SquareFactory)");
??}
}1.e测试类:请注意这个客户端程序多么简洁,既没有罗嗦的条件判断语句,也无需关心ConcreteProduct和ConcreteCreator的细节(因为这里我用anOperation封装了Product里的两个方法,所以连Product的影子也没看见,当然把Product里方法的具体调用放到客户程序中也是不错的).
class?Main?{
??public?static?void?main(String[]?args){
????ShapeFactory?sf1?=?new?SquareFactory();?
????ShapeFactory?sf2?=?new?CircleFactory();
????sf1.anOperation("Shape?one");
????sf2.anOperation("Shape?two");
??}
}??运行结果如下:
The current shape is: Shape one (created by SquareFactory)
It will draw a square.
It will erase a square.
The current shape is: Shape two (created by CircleFactory)
It will draw a circle.
It will erase a circle.
参数化的Factory Method:这种方式依靠指定的参数作为标志来创建对应的实例,这是很常见的一种办法.比如JFC中的BorderFactory就是个很不错的例子.以下的这个例子是用字符串作为标记来进行判断的,如果参数的类型也不一样,那就可以用到过载函数来解决这个问题,定义一系列参数和方法体不同的同名函数,这里java.util.Calendar.getInstance()又是个极好的例子.参数化的创建方式克服了FactoryMethod模式一个最显著的缺陷,就是当具体产品比较多时,我们不得不也建立一系列与之对应的具体构造器.但是在客户端我们必须指定参数来决定要创建哪一个类.2.a 我们在第一种方法的基础上进行修改,首先自定义一个的异常,这样当传入不正确的参数时可以得到更明显的报错信息.
class?NoThisShape?extends?Exception?{
??public?NoThisShape(String?aName)?{
????super(aName);
??}
}2.b去掉了ShapeFactory的两个子类,改为由ShapeFactory直接负责实例的创建. ShapeFactory自己变成一个具体的创建器,直接用参数化的方法实现factoryMethod返回多种对象.
abstract?class?ShapeFactory?{??
??private?static?Shape?s;
??private?ShapeFactory()?{}
????
??static?Shape?factoryMethod(String?aName,?String?aType)?throws?NoThisShape{
????if?(aType.compareTo("square")==0)
??????return?new?Square(aName);
????else?if?(aType.compareTo("circle")==0)
??????return?new?Circle(aName);
????else?throw?new?NoThisShape(aType);??
??}
??
??//?在anOperation中定义Shape的一系列行为
??static?void?anOperation(String?aName,?String?aType)?throws?NoThisShape{
????s?=?factoryMethod(aName,?aType);
????System.out.println("The?current?shape?is:?"?+?s.name);
????s.draw();
????s.erase();
??}
}2.c 测试类:这里客户端必须指定参数来决定具体创建哪个类.这个例子里的anOperation是静态函数,可以直接引用.
class?Main?{
??public?static?void?main(String[]?args)?throws?NoThisShape{
????ShapeFactory.anOperation("Shape?one","circle");
????ShapeFactory.anOperation("Shape?two","square");
????ShapeFactory.anOperation("Shape?three",?"delta");
??}
}运行结果如下:
The?current?shape?is:?Shape?one
It?will?draw?a?circle.
It?will?erase?a?circle.
The?current?shape?is:?Shape?two
It?will?draw?a?square.
It?will?erase?a?square.
Exception?in?thread?"main"?NoThisShape:?delta
????????at?ShapeFactory.factoryMethod(ShapeFactory.java:10)
????????at?ShapeFactory.anOperation(ShapeFactory.java:15)
????????at?Main.main(Main.java:5)动态装载机制:有的时候我们会把ConcreteProduct的实例传给创建器作为参数,这种情况下,如果在创建器里完成创建过程,就必须判断参数的具体类型(用instanceof),然后才能产生相应的实例,那么比较好的做法是利用Java的动态装载机制来完成这件事.比如:
我们得到一个Shape的子类s,但不知道具体是那个子类,就可以利用Class类自带的方法newInstance()得到实例
return (Shape)s.getClass().newInstance();
这种方法有兴趣得读者可以自己尝试,限于篇幅,不写具体代码出来了.
后话:
看完这篇文章后,相信读者对FactoryMethod模式有一个比较清楚的了解了.我想说的是,我们不仅应该关心一个具体的模式有什么作用,如何去实现这个模式,更应该透过现象看本质,不但知其然,还要知其所以然.要通过对模式的学习加深对面向对象思想的理解,让自己的认识得到升华.FactoryMethod模式看似简单,实则深刻.抽象,封装,继承,委托,多态,针对接口编程等面向对象中的概念都在这里得到了一一的体现.只有抓住了它的本质,我们才能够不拘于形式的灵活运用,而不是为了使用模式而使用模式.
?
参考资料
Thinking in Pattern with Java ---- Bruce Eckel
The Factory Method Design Pattern by Gopalan Suresh Raj---- http://gsraj.tripod.com/design/creational/factory/factory.html
SENG609_40 FACTORY PATTERNS PAPER---- http://sern.ucalgary.ca/~kjfu/courses/SENG60904/paper.html
Factory Method Pattern---- http:// www.ugolandini.net/FactoryMethodPattern.html
Design Patterns in Java ---- Bob Tarr
Design Patterns ---- Gang of Four
Dynamic Class Loading in Java---- http:// www.pramodx.20m.com/dynamic_class_loading_in_java.htm
?