zoukankan      html  css  js  c++  java
  • 工厂方法模式

    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类里,代码结构上清晰,扩展性好,代码量减少。

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

    我只是走在路上,偶尔驻足观赏,偶尔回头看看。
  • 相关阅读:
    C#笔记(Hex转JPG)
    rpm 和 yum 软件管理
    名称空间和作用域
    网络技术管理和进程管理
    RAID磁盘阵列
    CentOS7系统启动流程:
    磁盘lvm管理
    面向对象 异常处理
    自定义函数和调用函数 return返回值
    Python常用模块
  • 原文地址:https://www.cnblogs.com/zhuYears/p/2577794.html
Copyright © 2011-2022 走看看