工厂方法模式

GOF关于工厂方法的概念

1.1意图

定义一个用于创建对象的接口,让子类来决定实例化哪一个类。Factory Method使一个类的实例化延迟到其子类

1.2 别名

虚构造器(Virtual Constructor

1.3 实用性

同时满足下列情况下可以使用Factory Method模式:

  1. 当一个类不知道他所必须创建的类的对象的时候;
  2. 当一个类希望由它的子类来指定他所创建的对象的时候;
  3. 当类将创建对象的职责委托给多个帮助子类中的某一个,并且你希望将哪一个帮助子类是代理者这一信息局部化的时候。(后半句看不太明白,查了下原文,我个人理解的意思是“创建对象时,我们不必关心具体是由哪个子类来创建的”

1.4 结构

工厂方法结构图

1.5 参与者

    • Product:定义工厂方法所创建的对象的接口。
    • ConcreteProduct:实现Product接口。
    • Creator:1. 声明工厂方法,该方法返回一个Product类型的对象。Creator也可以定义一个工厂方法的缺省实现,它返回一个缺省的ConcreteProduct对象。 2. 可以调用工厂方法以创建一个Product对象。
    • ConcreteCreator:重定义工厂方法以返回一个ConcreteProduct

代码示例

人是铁,饭是钢。就简单举个一日三餐的例子,结构图如下:

工厂方法例子

代码如下:

   1: public interface Dinner
   2: {
   3:     /**
   4:      * 准备
   5:      * @see [类、类#方法、类#成员]
   6:      */
   7:     public void comprepare();
   8:     
   9:     /**
  10:      * 享用
  11:      * @see [类、类#方法、类#成员]
  12:      */
  13:     public void enjoy();
  14:     
  15:     /**
  16:      * 收拾
  17:      * @see [类、类#方法、类#成员]
  18:      */
  19:     public void clear();
  20: }
   1: public class Breakfast implements Dinner
   2: {
   3:     /**
   4:      * 面包
   5:      */
   6:     private String bread;
   7:     
   8:     /**
   9:      * 牛奶
  10:      */
  11:     private String milk;
  12:     
  13:     @Override
  14:     public void clear()
  15:     {
  16:         System.out.println("收拾早餐,还剩:" + this.milk + "和" + this.bread);
  17:     }
  18:     
  19:     @Override
  20:     public void comprepare()
  21:     {
  22:         this.milk = "一杯牛奶";
  23:         this.bread = "两个面包";
  24:         System.out.println("准备早餐:" + this.milk + "和" + this.bread);
  25:     }
  26:     
  27:     @Override
  28:     public void enjoy()
  29:     {
  30:         this.milk = "半杯牛奶";
  31:         this.bread = "一个面包";
  32:         System.out.println("享用早餐ing。。");
  33:     }
  34:     
  35: }
   1: public class Lunch implements Dinner
   2: {
   3:     /**
   4:      * 肉
   5:      */
   6:     private String meat;
   7:     
   8:     /**
   9:      * 米饭
  10:      */
  11:     private String rice;
  12:     
  13:     @Override
  14:     public void clear()
  15:     {
  16:         System.out.println("收拾午餐,还剩:" + this.meat + "和" + this.rice);
  17:     }
  18:     
  19:     @Override
  20:     public void comprepare()
  21:     {
  22:         this.meat = "两盘牛肉";
  23:         this.rice = "两碗米饭";
  24:         System.out.println("准备午餐:" + this.meat + "和" + this.rice);
  25:     }
  26:     
  27:     @Override
  28:     public void enjoy()
  29:     {
  30:         this.meat = "半盘牛肉";
  31:         this.rice = "半碗米饭";
  32:         System.out.println("享用午餐ing。。");
  33:     }
  34: }
   1: public class Supper implements Dinner
   2: {
   3:     /**
   4:      * 粥
   5:      */
   6:     private String congee;
   7:     
   8:     /**
   9:      * 炒青菜
  10:      */
  11:     private String greens;
  12:     
  13:     @Override
  14:     public void clear()
  15:     {
  16:         System.out.println("收拾晚餐,还剩:" + this.congee + "和" + this.greens);
  17:     }
  18:     
  19:     @Override
  20:     public void comprepare()
  21:     {
  22:         this.congee = "两碗粥";
  23:         this.greens = "一盘炒青菜";
  24:         System.out.println("准备晚餐:" + this.congee + "和" + this.greens);
  25:     }
  26:     
  27:     @Override
  28:     public void enjoy()
  29:     {
  30:         this.congee = "半碗粥";
  31:         this.greens = "半盘炒青菜";
  32:         System.out.println("享用晚餐ing。。");
  33:     }
  34: }
   1: public class DinnerFactory
   2: {
   3:     /**
   4:      * 正餐工厂方法
   5:      * @return
   6:      * @see [类、类#方法、类#成员]
   7:      */
   8:     public Dinner dinnerFactory()
   9:     {
  10:         //缺省做午餐
  11:         return new Lunch(); 
  12:     }
  13:     
  14:     public void newDinner()
  15:     {
  16:         Dinner dinner = dinnerFactory();
  17:         dinner.comprepare();
  18:         dinner.enjoy();
  19:         dinner.clear();
  20:     }
  21: }
   1: public class BreakfastFactory extends DinnerFactory
   2: {
   3:     @Override
   4:     public Dinner dinnerFactory()
   5:     {
   6:         return new Breakfast();
   7:     }
   8: }
   1: public class LunchFactory extends DinnerFactory
   2: {
   3:     @Override
   4:     public Dinner dinnerFactory()
   5:     {
   6:         return new Lunch();
   7:     }
   8:     
   9: }
   1: public class SupperFactory extends DinnerFactory
   2: {
   3:     @Override
   4:     public Dinner dinnerFactory()
   5:     {
   6:         return new Supper();
   7:     }
   8:     
   9: }
   1: public class Client
   2: {
   3:     
   4:     /**
   5:      * 测试
   6:      * @param args
   7:      * @see [类、类#方法、类#成员]
   8:      */
   9:     public static void main(String[] args)
  10:     {
  11:         DinnerFactory dinnerFactory = new DinnerFactory();
  12:         DinnerFactory breakfastFactory = new BreakfastFactory();
  13:         DinnerFactory lunchFactory = new LunchFactory();
  14:         DinnerFactory supperFactory = new SupperFactory();
  15:         Dinner dinner;
  16:         
  17:         //测试缺省状况下
  18:         dinner = dinnerFactory.dinnerFactory();
  19:         dinner.comprepare();
  20:         dinner.enjoy();
  21:         dinner.clear();
  22:         
  23:         System.out.println("--------------------------------------");
  24:         
  25:         //测试早餐
  26:         dinner = breakfastFactory.dinnerFactory();
  27:         dinner.comprepare();
  28:         dinner.enjoy();
  29:         dinner.clear();
  30:         
  31:         System.out.println("--------------------------------------");
  32:         
  33:         //测试newDinner方法
  34:         breakfastFactory.newDinner();
  35:         System.out.println("--------------------------------------");
  36:         lunchFactory.newDinner();
  37:         System.out.println("--------------------------------------");
  38:         supperFactory.newDinner();
  39:     }
  40: }

运行结果如下

   1: 准备午餐:两盘牛肉和两碗米饭
   2: 享用午餐ing。。
   3: 收拾午餐,还剩:半盘牛肉和半碗米饭
   4: --------------------------------------
   5: 准备早餐:一杯牛奶和两个面包
   6: 享用早餐ing。。
   7: 收拾早餐,还剩:半杯牛奶和一个面包
   8: --------------------------------------
   9: 准备早餐:一杯牛奶和两个面包
  10: 享用早餐ing。。
  11: 收拾早餐,还剩:半杯牛奶和一个面包
  12: 准备午餐:两盘牛肉和两碗米饭
  13: 享用午餐ing。。
  14: 收拾午餐,还剩:半盘牛肉和半碗米饭
  15: 准备晚餐:两碗粥和一盘炒青菜
  16: 享用晚餐ing。。
  17: 收拾晚餐,还剩:半碗粥和半盘炒青菜

工厂方法模式的应用

优点:

1. 良好的封装性,代码结构清晰。一个对象创建是有条件约束的,如一个调用者需要一个具体的产品对象,只要知道这个产品的类名(或约束字符串)就可以了,不用知道创建对象的艰辛过程,减少模块间的耦合。

2. 工厂方法模式的扩展性非常优秀。假如我们需要多吃一顿,只要修改具体的工厂类或者新增一个工厂类即可。

3. 屏蔽产品类。产品类的实现如何变化,调用者都不需要关心,它只需要关心产品的接口,只要接口保持不表,系统中的上层模块就不要发生变化,因为产品类的实例化工作是由工厂类负责,一个产品对象具体由哪一个产品生成是由工厂类决定的。

使用场景

首先,工厂方法模式是new一个对象的替代品,所以在所有需要生成对象的地方都可以使用,但是需要慎重地考虑是否要增加一个工厂类进行管理,增加代码的复杂度。

      其次,需要灵活的、可扩展的框架时,可以考虑采用工厂方法模式。万物皆对象,那万物也就皆产品类,例如需要设计一个连接邮件服务器的框架,有三种网络协议可供选择:POP3、IMAP、HTTP,我们就可以把这三种连接方法作为产品类,定义一个接口如IConnectMail,然后定义对邮件的操作方法,三个具体的产品类(也就是连接方式)进行不同的实现,再定义一个工厂方法,按照不同的传入条件,选择不同的连接方式。如此设计,可以做到完美的扩展,如某些邮件服务器提供了WebService接口,很好,我们只要增加一个产品类就可以了。

      再次,工厂方法模式可以用在异构项目中,例如通过WebService与一个非Java的项目交互,虽然WebService号称是可以做到异构系统的同构化,但是在实际的开发中,还是会碰到很多问题,如类型问题、WSDL文件的支持问题,等等,从WSDL中产生的对象都认为是一个产品,然后由一个具体的工厂类进行管理,减少与外围系统的耦合。

      最后,可以使用在测试驱动开发的框架下,例如,测试一个类A,就需要把与类A有关联关系的类B也同时产生出来,我们可以使用工厂方法模式把类B虚拟出来,避免类A与类B的耦合。目前由于JMock和EasyMock的诞生,该使用场景已经弱化了,读者可以在遇到此种情况时直接考虑使用JMock或 EasyMock。

引用自这里

参数化工厂方法和简单工厂

参数化工厂方法顾名思义,传入一个参数,通过参数来让工厂知道需要返回什么样的具体产品。根据上面一日三餐的例子,具体改变如下:

例子_参数

试想,当一个模块只需要一个工厂的时候,显然没有必要使用抽象类或者借口来声明工厂方法,再通过继承使子类定义或者重定义工厂方法。只需要使用静态方法来直接定义工厂方法就可以了,具体类图如下:

例子_简单

代码如下:

   1: public class DinnerFactory
   2: {
   3:     /**
   4:      * 正餐工厂方法
   5:      * @return
   6:      * @see [类、类#方法、类#成员]
   7:      */
   8:     public static Dinner dinnerFactory(String type)
   9:     {
  10:         if("breakfast".equals(type))
  11:         {
  12:             return new Breakfast();
  13:         }
  14:         if("lunch".equals(type))
  15:         {
  16:             return new Lunch();
  17:         }
  18:         if("supper".equals(type))
  19:         {
  20:             return new Supper();
  21:         }
  22:         return null;
  23:     }
  24: }

简单工厂是工厂方法模式的弱化,因为简单,所以被称为简单工厂模式(Simple Factory Pattern),也叫做静态工厂模式。在实际项目中,采用该方法的案例还是比较多的,其缺点是工厂类的扩展比较困难,不符合开闭原则,但它仍然是一个非常实用的设计模式。

Client类和newDinner方法

再来仔细研究下工厂方法模式的类图,会发现2个问题:

1. 在Creator类中除了工厂方法还有一个方法:AnOperation(),并做了注释,调用了工厂方法。

2. 没有发现其他模式类图中经常出现的Client类,也就是产品的使用者。

我没有准确的答案,在GOF关于Creator类的描述中,第二条是:可以调用工厂方法以创建一个Product对象。再看GOF关于Application和Document的例子,AnOperation方法其实是个模板方法,其中调用了工厂方法并且做了其他的一些行为。再结合GOF反复说明:“工厂方法通常在TemplateMethod中被调用”。

于是我这样理解:一个产品必须有对其使用者,也就是Client类。如果没有的话,那么我只能认为有其他的类达到了Client相同的作用。看一下工厂方法中的Creator类中的AnOperation方法,不正是产品Product的使用者吗?Factory Method突出的是对Procuct创建,所以在结构图中,没有出现额外的Client类,也不需要出现。

所以我模仿的在工厂类DinnerFactory中写了newDinner方法来封装整个吃饭的过程,至于Creator类为什么要在自己内部调用工厂方法,原因我依然不清楚,但是从最后的结果来看,把产品Product的行为都封装在Creator类里,代码结构上清晰,扩展性好,代码量减少。

 

欢迎大家提出建议,共同学习。

posted @ 2012-07-05 14:55  朱样年华  阅读(1405)  评论(3编辑  收藏  举报