副标题#e#
在设计模式中,Factory Method也是较量简朴的一个,但应用很是遍及,EJB,RMI,COM,CORBA,Swing中都可以看到此模式的影子,它是最重要的模式之一。在许多处所我们城市看到xxxFactory这样定名的类,那么,什么是Factory Method,为什么要用这个模式,如何用Java语言来实现该模式,这就是本文想要带给各人的内容。
根基观念
Factory Method是一种建设性模式,它界说了一个建设工具的接口,可是却让子类来抉择详细实例化哪一个类。当一个类无法预料要建设哪种类的工具或是一个类需要由子类来指定建设的工具时我们就需要用到Factory Method 模式了。
简朴说来,Factory Method可以按照差异的条件发生差异的实例,虽然这些差异的实例凡是是属于沟通的范例,具有配合的父类。Factory Method把建设这些实例的详细进程封装起来了,简化了客户端的应用,也改进了措施的扩展性,使得未来可以做最小的窜改就可以插手新的待建设的类。 凡是我们将Factory Method作为一种尺度的建设工具的要领,当发明需要更多的机动性的时候,就开始思量向其它建设型模式转化。
简朴阐明
图1是Factory Method 模式的布局图,这里提供了一些术语,让我们可以举办更利便的描写:
图1: Factory Method 模式布局
1.Product: 需要建设的产物的抽象类。
2.ConcreteProduct: Product的子类,一系列详细的产物。
3.Creator: 抽象建设器接口,声明返回Product范例工具的Factory Method。
#p#副标题#e#
4.ConcreteCreator: 详细的建设器,重写Creator中的Factory Method,返回ConcreteProduct范例的实例。
由此可以清楚的看出这样的平行对应干系:
Product <====> Creator ;
ConreteProduct <====> ConreteCreator
抽象产物对应抽象建设器,详细产物对应详细建设器。这样做的长处是什么呢?为什么我们不直接用详细的产物和详细的建设器完成需求呢?实际上我们也可以这样做。但通过Factory Method模式来完成,客户(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),而Factory Method模式就是利用了封装和委托的典规范子,这里封装是通过抽象建设器Creator来浮现的,而委托则是通过抽象建设器把建设工具的责任完全交给详细建设器ConcreteCreator来浮现的。
此刻,请再转头看看根基观念中的那段话,开始也许以为生涩难解,此刻是不是已经清朗化了许多。
下面让我们看看在 Java 中如何实现Factory Method模式,进一步加深对它的认识。
详细实施
先说明一点,用Factory Method模式建设工具并不必然会让我们的代码更短,实事上往往更长,我们也利用了更多的类,真正的目标在于这样可以机动的,有弹性的建设不确定的工具。并且,代码的可重用性提高了,客户端的应用简化了,客户措施的代码会大大淘汰,变的更具可读性。
尺度实现: 这里我回收Bruce Eckel 用来描写OO思想的经典例子 Shape。这样各人会较量熟悉一些。我完全凭据图1中所界说的布局写了下面的一段演示代码。这段代码的浸染是建设差异的Shape实例,每个实例完成两个操纵:draw和erase。详细的建设进程委托?ShapeFactory来完成。
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建设一个工具,并对该工具举办一系列操纵。
#p#分页标题#e#
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()又是个极好的例子。
参数化的建设方法降服了Factory Method模式一个最显著的缺陷,就是当详细产物较量多时,我们不得不也成立一系列与之对应的详细结构器。 可是在客户端我们必需指定参数来抉择要建设哪一个类。
2.a 我们在第一种要领的基本长举办修改,首先自界说一个的异常,这样当传入不正确的参数时可以获得更明明的报错信息。
class NoThisShape extends Exception
{
public NoThisShape(String aName)
{
super(aName);
}
}
2.b 去掉了ShapeFactory的两个子类,改为由ShapeFactory直接认真实例的建设。 ShapeFactory本身酿成一个详细的建设器,直接用参数化的要领实现factoryMethod返回多种工具。