WHAT

定义

Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.

(定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方式使一个类的实例化延迟到其子类。)

通用类图

Product:抽象的产品类

定义了产品的共性,实现对事物最抽象的定义

Creator:抽象创建类

抽象工厂

ConcreteCreator:具体的实现工厂

定义具体如何产生具体的产品

WHY

优点

  • 良好的封装性,代码结构清晰
    • 一个对象创建时有条件约束的
    • 只要知道产品的类名就可以,不需要知道具体如何创建对象,降低模块之间的耦合
  • 扩展性非常优秀
    • 增加产品类的情况下,需要适当地修改决堤的工厂类或扩展一个工厂类,就可以完成修改。
  • 屏蔽产品类
    • 产品类的实现如何变化,调用者不需要关心,只需要关心产品的接口,只要接口保持不变,系统中的上层模块就不会发生变化
    • 数据库开发中,如果说那个JDBC连接数据库,从MySQL切换到Oracle,需要改动的地方就是切换一下驱动名称
  • 典型的解耦框架
    • 高层模块只需要知道产品的抽象类,其他的实现类都不用关心,符合迪米特法则
    • 只依赖产品类的抽象,符合依赖倒置原则
    • 使用产品子类替换产品父类,符合里氏替换原则

缺点

  • 增加了代码的复杂度

HOW

使用场景

  • 工厂方法模式是new一个对象的替代品,所以在所有需要生成对象的地方都可以使用
    • 但需要慎重考虑是否增加一个工厂类进行管理,会增加代码的复杂度
  • 需要灵活的、可扩展的框架时,可以考虑采用工厂方法模式
    • 数据库驱动 - MySQL、Oracle等
    • 设计一个连接邮件服务器的框架
      • 有三种网络协议可供选择:POP3、IMAP、HTTP,作为具体的产品类
      • IConnectMail,作为抽象产品类,定义对邮件的操作方法
      • 定义抽象工厂
      • 实现具体工厂
      • 如果需要增加WebService接口,只需要增加一个产品类就好了
  • 工厂方法模式可以用在异构项目中
    • 通过WebService与一个非Java的项目交互

扩展

  • 简单工厂模式 (Simple Factory Pattern)

    • 也称作:静态工厂模式
    • 优点
      • 简化继承抽象类
      • 将创建方法设置为静态类型,简化类的创建过程
    • 缺点
      • 扩展比较困难
      • 不符合开闭原则
  • 多个工厂类

    • 场景
      • 一个产品类有5个具体实现,每个实现类的初始化方法都不相同,如果写在一个工厂方法中,势必会导致该方法巨大无比
    • 操作
      • 为每个产品定义一个创造者,然后由调用者自己去选择与哪个工厂方法关联
    • 实现
      • interface IProduct - 定义产品类的公用方法
      • ProductA - 定义具体的产品类
      • AbstractProductFactory - 定义产品的抽象工厂
      • ProductAFactory - 具体的产品A的工厂
    • TIPS
      • 抽象方法中不再需要传递相关参数,因为每个具体的工厂都已经非常明确自己的职责:创建自己负责的产品类对象
      • 复杂应用中一般采用多工厂的方法,再增加一个协调类,避免调用者与各个子工厂交流,就是封装了子工厂类,对高层模块提供统一的访问接口。
    • 优势
      • 创建类的职责清晰,结构简单
    • 劣势
      • 降低了可扩展性和可维护性
        • 要扩展一个产品类,需要建立一个相应的工厂类,增加了扩展的难度
  • 替代单例模式

    • 单例模式的核心要求是内存中只有一个对象,通过工厂方法模式也可以只在内存中生产一个对象

    • 类图

    • 单例类不能通过正常渠道建立一个对昂,那个单例工厂通过如何创建一个单例对象呢?

      • 通过反射方式

        public class SingletonFactory{
          private static Singleton singleton;
          static {
            try {
              Class cl = Class.forName(Singleton.class.getName());
              // 获得无参构造
              Constructor constructor = cl.getDeclaredConstructor();
              //设置无参构造是可访问的
              constructor.setAccessible(true);
              //产生一个实例对象
              singleton = (Singleton)constructor.newInstance();
            }catch(Exception e){
              // 异常处理
            }
          }
          public static Singleton getSingleton(){
            return singleton;
          }
        }
        
  • 延迟初始化 - Lazy initialization

    • 一个对象被消费完毕后,并不立即释放,工厂类保持其初始状态,等待再次被使用

    • 类图

      • 通过定义一个Map容器,容纳所有生成的对象;如果有则取出返回,如果没有则创建后放入Map中再放回
    • 可以继续扩展

      • 限制某一个产品类的最大实例化数量
    • 使用场景

      • 设置产品类的最大实例化数量
      • 对象初始化比较复杂的情况下,降低对象产生和销毁带来的复杂性

最佳实践

熟练掌握工厂方法模式,多思考工厂方法如何应用,而且工厂方法模式还可以与其他模式混合使用(例如模板方法模式、单例模式、原型模式等)。


《设计模式之禅》第八章 工厂方法模式