分类: Java
2011-05-31 22:59:22
FactoryMethod是一种创建性模式,它定义了一个创建对象的接口,但是却让子类来决定具体实例化哪一个类.当一个类无法预料要创建哪种类的对象或是一个类需要由子类来指定创建的对象时我们就需要用到Factory Method 模式了.简单说来,Factory Method可以根据不同的条件产生不同的实例,当然这些不同的实例通常是属于相同的类型,具有共同的父类.Factory Method把创建这些实例的具体过程封装起来了,简化了客户端的应用,也改善了程序的扩展性,使得将来可以做最小的改动就可以加入新的待创建的类. 通常我们将Factory Method作为一种标准的创建对象的方法,当发现需要更多的灵活性的时候,就开始考虑向其它创建型模式转化
图1是Factory Method 模式的结构图,这里提供了一些术语,让我们可以进行更方便的描述:
由此可以清楚的看出这样的平行对应关系: Product <====> Creator ; ConreteProduct <====> ConreteCreator
抽象产品对应抽象创建器,具体产品对应具体创建器.这样做的好处是什么呢?为什么我们不直接用具体的产品和具体的创建器完成需求呢?实际上我们也可以这样做.但通过Factory Method模式来完成,客户(client)只需引用抽象的Product和Creater,对具体的ConcreteProduct和ConcreteCreator可以毫不关心,这样做我们可以获得额外的好处:
现在,请再回头看看基本概念中的那段话,开始也许觉得生涩难懂,现在是不是已经明朗化了很多.
下面让我们看看在 Java 中如何实现Factory Method模式,进一步加深对它的认识.
先说明一点,用Factory Method模式创建对象并不一定会让我们的代码更短,实事上往往更长,我们也使用了更多的类,真正的目的在于这样可以灵活的,有弹性的创建不确定的对象.而且,代码的可重用性提高了,客户端的应用简化了,客户程序的代码会大大减少,变的更具可读性.
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.
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();
这种方法有兴趣得读者可以自己尝试,限于篇幅,不写具体代码出来了.
看完这篇文章后,相信读者对Factory Method模式有一个比较清楚的了解了.我想说的是,我们不仅应该关心一个具体的模式有什么作用,如何去实现这个模式,更应该透过现象看本质,不但知其然,还要知其所以然.要通过对模式的学习加深对面向对象思想的理解,让自己的认识得到升华.Factory Method模式看似简单,实则深刻.抽象,封装,继承,委托,多态,针对接口编程等面向对象中的概念都在这里得到了一一的体现.只有抓住了它的本质,我们才能够不拘于形式的灵活运用,而不是为了使用模式而使用模式.