技术开发 频道

在Java中应用设计模式Factory Method

  先说明一点,用Factory Method模式创建对象并不一定会让我们的代码更短,实事上往往更长,我们也使用了更多的类,真正的目的在于这样可以灵活的,有弹性的创建不确定的对象.而且,代码的可重用性提高了,客户端的应用简化了,客户程序的代码会大大减少,变的更具可读性.

  标准实现: 这里我采用Bruce Eckel 用来描述OO思想的经典例子 Shape.这样大家会比较熟悉一些.我完全按照图1中所定义的结构写了下面的一段演示代码.这段代码的作用是创建不同的Shape实例,每个实例完成两个操作:draw和erase.具体的创建过程委托?oShapeFactory来完成.

  1.a 首先定义一个抽象类Shape,定义两个抽象的方法.

1  abstract class Shape {
2
3   // 勾画shape
4
5   public abstract void draw();
6
7   // 擦去 shape
8
9   public abstract void erase();
10
11   public String name;
12
13   public Shape(String aName){
14
15   name = aName;
16
17   }
18
19   }
20
21

  1.b 定义 Shape的两个子类: Circle, Square,实现Shape中定义的抽象方法

1 // 圆形子类
2
3   class Circle extends Shape {
4
5   public void draw() {
6
7   System.out.println("It will draw a circle.");
8
9   }
10
11   public void erase() {
12
13   System.out.println("It will erase a circle.");
14
15   }
16
17   // 构造函数
18
19   public Circle(String aName){
20
21   super(aName);
22
23   }
24
25   }
26
27   // 方形子类
28
29   class Square extends Shape {
30
31   public void draw() {
32
33   System.out.println("It will draw a square.");
34
35   }
36
37   public void erase() {
38
39   System.out.println("It will erase a square.");
40
41   }
42
43   // 构造函数
44
45   public Square(String aName){
46
47   super(aName);
48
49   }
50
51   }
52
53

  1.c 定义抽象的创建器,anOperation调用factoryMethod创建一个对象,并对该对象进行一系列操作.

1 abstract class ShapeFactory {
2
3   protected abstract Shape factoryMethod(String aName);
4
5   // 在anOperation中定义Shape的一系列行为
6
7   public void anOperation(String aName){
8
9   Shape s = factoryMethod(aName);
10
11   System.out.println("The current shape is: " + s.name);
12
13   s.draw();
14
15   s.erase();
16
17   }
18
19   }
20
21

  1.d 定义与circle和square相对应的两个具体创建器CircleFactory,SquareFactory,实现父类的methodFactory方法

1  // 定义返回 circle 实例的 CircleFactory
2
3   class CircleFactory extends ShapeFactory {
4
5   // 重载factoryMethod方法,返回Circle对象
6
7   protected Shape factoryMethod(String aName) {
8
9   return new Circle(aName + " (created by CircleFactory)");
10
11   }
12
13   }
14
15   // 定义返回 Square 实例的 SquareFactory
16
17   class SquareFactory extends ShapeFactory {
18
19   // 重载factoryMethod方法,返回Square对象
20
21   protected Shape factoryMethod(String aName) {
22
23   return new Square(aName + " (created by SquareFactory)");
24
25   }
26
27   }
28
29

  1.e 测试类:请注意这个客户端程序多么简洁,既没有罗嗦的条件判断语句,也无需关心ConcreteProduct和ConcreteCreator的细节(因为这里我用anOperation封装了Product里的两个方法,所以连Product的影子也没看见,当然把Product里方法的具体调用放到客户程序中也是不错的).

1 class Main {
2
3   public static void main(String[] args){
4
5   ShapeFactory sf1 = new SquareFactory();
6
7   ShapeFactory sf2 = new CircleFactory();
8
9   sf1.anOperation("Shape one");
10
11   sf2.anOperation("Shape two");
12
13   }
14
15   }
16
17

  运行结果如下:

1 The current shape is: Shape one (created by SquareFactory)
2
3   It will draw a square.
4
5   It will erase a square.
6
7   The current shape is: Shape two (created by CircleFactory)
8
9   It will draw a circle.
10
11   It will erase a circle.
12
13

  参数化的Factory Method: 这种方式依靠指定的参数作为标志来创建对应的实例,这是很常见的一种办法.比如JFC中的BorderFactory就是个很不错的例子. 以下的这个例子是用字符串作为标记来进行判断的,如果参数的类型也不一样,那就可以用到过载函数来解决这个问题,定义一系列参数和方法体不同的同名函数,这里java.util.Calendar.getInstance()又是个极好的例子.参数化的创建方式克服了Factory Method模式一个最显著的缺陷,就是当具体产品比较多时,我们不得不也建立一系列与之对应的具体构造器. 但是在客户端我们必须指定参数来决定要创建哪一个类.

  2.a 我们在第一种方法的基础上进行修改,首先自定义一个的异常,这样当传入不正确的参数时可以得到更明显的报错信息.

1 class NoThisShape extends Exception {
2
3   public NoThisShape(String aName) {
4
5   super(aName);
6
7   }
8
9   }
10
11

  2.b去掉了ShapeFactory的两个子类,改为由ShapeFactory直接负责实例的创建. ShapeFactory自己变成一个具体的创建器,直接用参数化的方法实现factoryMethod返回多种对象.

1 abstract class ShapeFactory {
2
3   private static Shape s;
4
5   private ShapeFactory() {}
6
7   static Shape factoryMethod(String aName, String aType) throws NoThisShape{
8
9   if (aType.compareTo("square")==0)
10
11   return new Square(aName);
12
13   else if (aType.compareTo("circle")==0)
14
15   return new Circle(aName);
16
17   else throw new NoThisShape(aType);
18
19   }
20
21   // 在anOperation中定义Shape的一系列行为
22
23   static void anOperation(String aName, String aType) throws NoThisShape{
24
25   s = factoryMethod(aName, aType);
26
27   System.out.println("The current shape is: " + s.name);
28
29   s.draw();
30
31   s.erase();
32
33   }
34
35   }
36
37

  2.c 测试类:这里客户端必须指定参数来决定具体创建哪个类.这个例子里的anOperation是静态函数,可以直接引用.

1  class Main {
2
3   public static void main(String[] args) throws NoThisShape{
4
5   ShapeFactory.anOperation("Shape one","circle");
6
7   ShapeFactory.anOperation("Shape two","square");
8
9   ShapeFactory.anOperation("Shape three", "delta");
10
11   }
12
13   }
14
15

  运行结果如下:

1  class Main {
2
3   public static void main(String[] args) throws NoThisShape{
4
5   ShapeFactory.anOperation("Shape one","circle");
6
7   ShapeFactory.anOperation("Shape two","square");
8
9   ShapeFactory.anOperation("Shape three", "delta");
10
11   }
12
13   }
14
15

  动态装载机制:

  有的时候我们会把ConcreteProduct的实例传给创建器作为参数,这种情况下,如果在创建器里完成创建过程,就必须判断参数的具体类型(用instanceof),然后才能产生相应的实例,那么比较好的做法是利用Java的动态装载机制来完成这件事.比如:

  我们得到一个Shape的子类s,但不知道具体是那个子类,就可以利用Class类自带的方法newInstance()得到实例

  return (Shape)s.getClass().newInstance();

  总结

  看完这篇文章后,相信读者对Factory Method模式有一个比较清楚的了解了.我想说的是,我们不仅应该关心一个具体的模式有什么作用,如何去实现这个模式,更应该透过现象看本质,不但知其然,还要知其所以然.要通过对模式的学习加深对面向对象思想的理解,让自己的认识得到升华.Factory Method模式看似简单,实则深刻.抽象,封装,继承,委托,多态,针对接口编程等面向对象中的概念都在这里得到了一一的体现.只有抓住了它的本质,我们才能够不拘于形式的灵活运用,而不是为了使用模式而使用模式.

0
相关文章