13.工厂方法设计模式

483 阅读6分钟

工厂设计模式

一. 回顾简单工厂设计模式

简单工厂设计模式: juejin.cn/post/696642…

前面我们研究了简单工厂设计模式, 还研究了设计模式的6大原则. 结合设计模式的六大原则, 我们来回顾一下简单工厂设计模式。

我们使用计算为例, 如果使用简单工厂设计模式, 如何实现呢?来看这个UML图

第一步: 定义一个抽象的接口, 里面有待实现的方法 第二步:定义抽象接口的实现类 第三步:定义工厂方法, 传入一个类型参数 第四步:客户端调用, 执行哪种计算, 给工厂方法传入类型参数即可.

这样, 一个简单工厂就是实现了. 可是, 根据设计模式6大原则, 我们来分析, 简单工厂存在哪些问题

  1. 符合单一职责原则: 一个类只做一件事
  2. 符合里式替换原则: 父类出现的地方都可以使用子类替换
  3. 符合依赖倒置原则: 依赖于抽象, 而不是依赖于具体
  4. 符合接口隔离原则: 类和类之间建立在最小接口上
  5. 符合迪米特法则: 和其他类有最少的关联.
  6. 不符合开闭原则: 对扩展开发, 对修改关闭. 为什么不符合呢? 假如, 我们要增加一种计算类型---取平方. 首先要先增加一个取平方的实现类, 然后修改工厂方法. 这样就修改了原来的方法, 修改原来的方法就有可能引入错误. 所以, 不符合开闭原则.

简单工厂设计的缺点

  1. 工厂类集中了所有实例对象的创建逻辑,一旦工厂不能正常工作,整个系统都会受到影响;耦合性太高.
  2. 违背“开放 - 关闭原则”,新添加产品就要修改原来工厂类的逻辑,这样会使工厂逻辑越来越复杂, 且容易引发错误.
  3. 简单工厂模式使用了静态工厂方法,而静态方法不能被继承和重写,这会造成工厂角色无法形成基于继承的等级结构。

基于以上几点, 我们引入了一个新的设计模式来解决这些问题---"工厂方法设计模式"

二. 工厂方法设计模式

工厂模式, 刚好是简单工厂模式的补充. "工厂方法模式"对简单工厂模式进一步进行了抽象化,其好处是可以使系统在不修改原来代码的情况下引进新的产品,即满足开闭原则。

来看看工厂方法设计模式的UML图, 这里是以计算数字为例:

针对简单工厂的缺点, 我们将工厂进行了进一步的抽象. 抽象出一个计算工厂. 计算工厂有很多子工厂, 每个子工厂用来创建相关类别的类.

接下来看看源码:

/**
 * 计算抽象类
 */
public interface ICalculate {

    public int result(int num1, int num2);
}

/**
 * 加法运算
 */
public class Add implements ICalculate {

    /**
     * 两个数相加
     * @param num1
     * @param num2
     * @return
     */
    @Override
    public int result(int num1, int num2) {
        return num1 + num2;
    }

}

/**
 * 减法运算
 */
public class Sub implements ICalculate {

    /**
     * 两个数相减
     * @param num1
     * @param num2
     * @return
     */
    @Override
    public int result(int num1, int num2) {
        return num1 - num2;
    }

}

/**
 * 乘法运算
 */
public class Mul implements ICalculate {

    /**
     * 两个数相乘
     * @param num1
     * @param num2
     * @return
     */
    @Override
    public int result(int num1, int num2) {
        return num1 * num2;
    }

}

/**
 * 除法运算
 */
public class Div implements ICalculate {

    /**
     * 两个数相除
     * @param num1
     * @param num2
     * @return
     */
    @Override
    public int result(int num1, int num2) {
        return num1 / num2;
    }

}

/**
 * 计算工厂
 */
public interface ICalculateFactory {
    ICalculate create();
}

/**
 * 加法工厂
 */
public class AddFactory implements ICalculateFactory {

    @Override
    public ICalculate create() {
        return new Add();
    }
}

/**
  * 减法工厂
  */
 public class SubFactory implements ICalculateFactory {
 
     @Override
     public ICalculate create() {
         return new Sub();
     }
 }

/**
 * 乘法工厂
 */
public class MulFactory implements ICalculateFactory {

    @Override
    public ICalculate create() {
        return new Sub();
    }
}

/**
 * 除法工厂
 */
public class DivFactory implements ICalculateFactory {

    @Override
    public ICalculate create() {
        return new Sub();
    }
}

/**
 * 客户端调用
 */
public class Client {
    public static void main(String[] args) {
        ICalculateFactory factory = new AddFactory();
        int result = factory.create().result(12, 23);
        System.out.println(result);
    }
}

假如现在业务扩展了, 要计算平方, 这时, 我们只需要增加平法计算实现类和平方工厂类. 而无需修改原来的代码. 满足开闭原则. 这种方式也有缺点, 那就是类增加了很多.

三. 工厂方法设计模式的主要作用

将类的实例化(具体产品的创建)延迟到工厂类的子类(具体工厂)中完成,即由子类来决定应该实例化(创建)哪一个类。

四. 搭建工厂方法设计模式的步骤

第一步: 抽象产品(Product), 描述具体产品的公共接口 第二步: 具体产品(Concrete Product), 描述生产的具体产品 第三步: 抽象工厂(Creator), 述具体工厂的公共接口 第四步: 具体工厂(Concrete Creator), 描述具体工厂;实现抽象工厂方法创建产品的实例 第五步: 外界通过调用具体工厂类的方法,从而创建不同具体产品类的实例

五. 工厂方法设计模式的优缺点

1. 优点

  • 更符合开闭原则
    新增一种产品时,只需要增加相应的具体产品类和相应的工厂子类即可

  • 符合单一职责原则
    每个具体工厂类只负责创建对应的产品

  • 不使用静态工厂方法,可以形成基于继承的等级结构。

总结:工厂模式可以说是简单工厂模式的进一步抽象和拓展,在保留了简单工厂的封装优点的同时,让扩展变得简单,让继承变得可行,增加了多态性的体现。

2. 缺点

  1. 添加新产品时,除了增加新产品类外,还要提供与之对应的具体工厂类,系统类的个数将成对增加,在一定程度上增加了系统的复杂度;同时,有更多的类需要编译和运行,会给系统带来一些额外的开销;

  2. 由于考虑到系统的可扩展性,需要引入抽象层,在客户端代码中均使用抽象层进行定义,增加了系统的抽象性和理解难度,且在实现时可能需要用到DOM、反射等技术,增加了系统的实现难度。

  3. 虽然保证了工厂方法内的对修改关闭,但对于使用工厂方法的类,如果要更换另外一种产品,仍然需要修改实例化的具体工厂类; 一个具体工厂只能创建一种具体产品