工厂方法模式
文章目录
工厂模式细分为三种类型:
- 简单工厂
- 工厂方法
- 抽象工厂
简单工厂是工厂方法的简化。
注意,工厂类的模式用来创建产品对象,这里的创建不一定真的是申请内存创建新对象,也可能是维护对象池,由工厂返回空闲对象。
介绍
工厂方法:
- 有一个抽象的产品的接口
- 若干个产品类以不同方式实现了这个产品接口
- 定义一个用来创建产品的工厂接口,包含一个创建抽象产品的方法
- 若干个工厂类以不同方式实现这个工厂接口,创建产品的方法返回抽象产品的某种具体实现,工厂各自生产自己的产品。
定义一个用于创建抽象产品对象的接口,让子类决定实例化成哪一个类。 工厂方法使一个类的实例化延迟到其子类。
当增加一个产品时,只需要增加一个工厂接口的子类,满足开闭原则
,解决简单工厂生产产品种类过多时导致的内部代码臃肿(switch-case过多)的问题。
- 标准的工厂方法需要有一个工厂类接口,即UML中的Creator。
- 这个接口的作用是为了实现
里氏替换原则:子类对象能够代替程序中父类对象出现的任何地方
,保证代码的优雅。
- 这个接口的作用是为了实现
- 抽象的产品接口,即UML中的Product,它的存在有三重含义
- 表明工厂方法创建的产品功能应该是类似的,可以抽象出一个父类
- 符合里氏替换原则,使代码优雅
- 符合
依赖倒置原则:高层模块不要依赖底层模块
。高层模块和高层模块之间进行通信,如Product和Creator。高层模块和低层模块应该通过抽象来互相依赖。
简单工厂
当产品种类固定,后续不会扩展时,可以使用简单工厂模式
:
- 工厂类只有一个,没有工厂接口
- 使用一个工厂类创建各种产品,创建方法返回Product接口
- 工厂类根据参数决定生产哪种产品
简单工厂模式下,如果后期新增产品,需要修改工厂类,不满足开闭原则
。这种情况下就要使用工厂方法
了。
简单工厂和工厂方法对比:
- 简单工厂缺一个工厂接口,只有一个真实的工厂。导致和工厂方法在实现和使用场景上都有区别
- 实现上:简单工厂直接返回ConcreteProduct对象;工厂方法先获取工厂对象,然后通过工厂对象创建ConcreateProduct
- 场景上:如果后续会增加Product,就用工厂方法,增加一个工厂类即可;如果Product类别固定,直接使用简单工厂
使用场景
- 在编写代码的过程中,如果无法预知对象确切类别及其依赖关系时,可使用工厂方法。
- 工厂方法将创建产品的代码与实际使用产品的代码分离,从而能在不影响其他代码的情况下扩展产品创建部分代码。
- 例如,如果需要向应用中添加一种新产品,只需要开发新的创建者子类,然后重写其工厂方法即可。
- 如果希望用户能扩展软件库或框架的内部组件,可使用工厂方法。
- 框架主干逻辑使用工厂接口创建元素对象
- 当用户需要扩展元素行为时,开发新的工厂来返回拥有新行为的元素
- 如果希望复用现有对象来节省系统资源,而不是每次都重新创建对象,可使用工厂方法。
- 使用工厂方法并不意味着申请内存创建新的对象,还可以复用空闲对象,比如数据库连接、文件系统和网络资源
优缺点
优点
- 避免创建者和具体产品之间的紧密耦合。
- 单一职责原则。将产品创建代码放在程序的单一位置,从而使得代码更容易维护。
- 开闭原则。无需更改现有客户端代码,就可以在程序中引入新的产品类型(工厂方法模式)。
缺点
- 需要引入许多新的子类,代码可能会因此变得更复杂。
示例
对于不同场景,需要创建不同的对象,但这些对象的功能很相似,可以抽象成一个父类。
在实际中,很多框架都支持多种配置文件,项目启动时解析配置文件,将文件内容写入到内存中。配置文件格式很多,有xml、json、yaml等,这个时候就需要根据后缀来解析文件,使用工厂模式就很合理。提高了灵活性。
工厂方法是里氏替换原则
、依赖倒置原则
、开放-封闭
原则的体现。在具体实现上,主要使用了接口与实现的语法。
简单工厂和工厂方法在开放-封闭原则
上是一致的
- 支持增加新的解析器,对于扩展是开放的,不会影响以前的代码
- 但是需要分别在create方法中加后缀的判断,一定程度上都影响了封闭原则
- 如果不想违背封闭原则,可以改为用配置文件的方式,提升封闭性
|
|
|
|
|
|
|
|
产品种类不会增加用简单工厂,否则用工厂方法。
总结
工厂方法是里氏替换原则、依赖倒转原则、开放-封闭原则的体现。在具体实现上,主要使用了接口与实现的语法。
其实工厂方法提高了灵活性,假设你开发了一个框架,只能解析yaml,代码开源到github上,这时会有很多小伙伴来增强你的代码, 如果解析方面你使用了工厂方法,那么其他人能够很方便的添加新的解析功能,同时对原有逻辑影响降到最低,这就是优雅。