工厂设计模式
一. 回顾简单工厂设计模式
简单工厂设计模式: juejin.cn/post/696642…
前面我们研究了简单工厂设计模式, 还研究了设计模式的6大原则. 结合设计模式的六大原则, 我们来回顾一下简单工厂设计模式。
我们使用计算为例, 如果使用简单工厂设计模式, 如何实现呢?来看这个UML图
第一步: 定义一个抽象的接口, 里面有待实现的方法 第二步:定义抽象接口的实现类 第三步:定义工厂方法, 传入一个类型参数 第四步:客户端调用, 执行哪种计算, 给工厂方法传入类型参数即可.
这样, 一个简单工厂就是实现了. 可是, 根据设计模式6大原则, 我们来分析, 简单工厂存在哪些问题
- 符合单一职责原则: 一个类只做一件事
- 符合里式替换原则: 父类出现的地方都可以使用子类替换
- 符合依赖倒置原则: 依赖于抽象, 而不是依赖于具体
- 符合接口隔离原则: 类和类之间建立在最小接口上
- 符合迪米特法则: 和其他类有最少的关联.
- 不符合开闭原则: 对扩展开发, 对修改关闭. 为什么不符合呢? 假如, 我们要增加一种计算类型---取平方. 首先要先增加一个取平方的实现类, 然后修改工厂方法. 这样就修改了原来的方法, 修改原来的方法就有可能引入错误. 所以, 不符合开闭原则.
简单工厂设计的缺点
- 工厂类集中了所有实例对象的创建逻辑,一旦工厂不能正常工作,整个系统都会受到影响;耦合性太高.
- 违背“开放 - 关闭原则”,新添加产品就要修改原来工厂类的逻辑,这样会使工厂逻辑越来越复杂, 且容易引发错误.
- 简单工厂模式使用了静态工厂方法,而静态方法不能被继承和重写,这会造成工厂角色无法形成基于继承的等级结构。
基于以上几点, 我们引入了一个新的设计模式来解决这些问题---"工厂方法设计模式"
二. 工厂方法设计模式
工厂模式, 刚好是简单工厂模式的补充. "工厂方法模式"对简单工厂模式进一步进行了抽象化,其好处是可以使系统在不修改原来代码的情况下引进新的产品,即满足开闭原则。
来看看工厂方法设计模式的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. 缺点
-
添加新产品时,除了增加新产品类外,还要提供与之对应的具体工厂类,系统类的个数将成对增加,在一定程度上增加了系统的复杂度;同时,有更多的类需要编译和运行,会给系统带来一些额外的开销;
-
由于考虑到系统的可扩展性,需要引入抽象层,在客户端代码中均使用抽象层进行定义,增加了系统的抽象性和理解难度,且在实现时可能需要用到DOM、反射等技术,增加了系统的实现难度。
-
虽然保证了工厂方法内的对修改关闭,但对于使用工厂方法的类,如果要更换另外一种产品,仍然需要修改实例化的具体工厂类; 一个具体工厂只能创建一种具体产品