【实战指南】设计模式 - 工厂模式

本文涉及的产品
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
云原生网关 MSE Higress,422元/月
任务调度 XXL-JOB 版免费试用,400 元额度,开发版规格
简介: 工厂模式是一种面向对象设计模式,通过定义“工厂”来创建具体产品实例。它包含简单工厂、工厂方法和抽象工厂三种形式,分别适用于不同复杂度的场景。简单工厂便于理解但扩展性差;工厂方法符合开闭原则,适合单一类型产品创建;抽象工厂支持多类型产品创建,但不便于新增产品种类。三者各有优缺点,适用于不同设计需求。

工厂模式是一种面向对象设计模式,定义了 工厂 的概念,软件设计时抽象产品派生出产品子类,具体的产品实例由 工厂 创建,用户从 工厂 申请需要的产品实例来使用。

简单工厂

简单工厂的概念并不存在于23种设计模式之中,学习的过程中发现其实现原理有助于理解本文后面两种设计模式。这里就列举出来。

其实现原理是,先抽象出产品的基类,然后由基类派生出各种产品子类。工厂 仅需要设计业务,由输入参数来确定创建哪种产品实例 注1

场景列举:

假设一个场景: 某手机厂商旗下有Mate、Nova和P等品牌,不同的品牌功能相同但参数有所差异,工厂拥有生产所有品牌手机的能力。当Mate品牌发布新机,工厂需要生产Mate品牌;类似的,当其他品牌发布新机,工厂也需要生产其他品牌。那么从面向对象设计的角度,如何将此场景设计出来呢?

解决: 首先将类定义出来,涉及到Mate、Nova、P和工厂类,工厂的接口返回手机的实例。那么问题来了,从软件角度来思考,工厂的一个接口只能返回一种类的实例,如何让一个接口返回多个不同类的实例呢?C++基类虚函数可以解决这个问题注2。因此将Mate、Nova和P抽象出一个基类CPhone,工厂接口类型指定为CPhone *。加入逻辑判断,达到不同的参数返回不同的手机实例效果。

类图

使用方法: 当需要生产某个品牌手机时,只需要在工厂方法getPhoneInstance指定product即可获取到某品牌实例。

缺点:

  • 当增加新的手机品牌时,需要更改工厂方法getPhoneInstance来兼容新的品牌,不符合开闭原则注3

工厂方法

由于简单工厂的缺点是不可忽视的,因此对简单工厂进行优化从而产生 工厂方法 的概念。从简单方法的类图中可以发现,工厂方法getPhoneInstance需要优化。

解决:由多个工厂子类替换掉参数的做法。将工厂类派生出多个工厂子类,一个工厂子类对应一个产品子类。如此一来,增加一个产品时,仅需要增加一个工厂子类即可,不需要对之前的逻辑进行修改。

类图:

使用方法: 当需要生产某种品牌时,用户只需要由new对应的工厂子类实例,由getPhoneInstance获取此品牌实例。若增加新品牌,需要工厂派生新的工厂子类,对应的产品派生出新的产品子类,由新的工厂实例getPhoneInstance返回新的产品实例即可。

缺点:

  • 当出现新的产品时,就需要发布新的工厂子类,长此以往工厂实例会越来越庞大,对使用者不友好。

抽象工厂

工厂方法多用于同一类型的产品生产,即仅存在一个抽象产品基类。假设业务需要,工厂要增加新产品(Watch)。此时 工厂方法 的设计无法满足需求,由此又提出了抽象工厂的概念。

解决方法: 新增产品基类CWatch派生出产品子类CHwGt2、CHwGt2Pro,同时在工厂基类中增加获取该子类实例的接口。如此一来,每个工厂都具备生产CPhone和CWatch的能力。

类图:

使用方法:与工厂方法类似,先new出对应的工厂子类实例,然后调用getPhoneInstance和getWatchInstance获取对应的产品实例。

缺点:

  • 当新增一个不同于已存在的产品种类时(如notebook),需要在工厂基类及所有子类中增加getNoteBookInstance的接口。若存在大量的工厂子类,这种操作是存在风险的,也不符合开闭原则 注3

总结

  • 工厂方法属于创建型模式,主要用于规范类实例的创建。
  • 简单工厂用法简单但是不符合开闭原则,设计中不要采用此种方式。工厂方法适合于单一类型的产品实例。抽象工厂适合于创建多个已知类型产品实例,不适用于增加新的产品类型。
  • 几种工厂方式都存在一定缺陷,在长期使用过程中会创建过多子类,可能会造成代码难以维护。对于少数量的产品,是很实用的技巧。
  • 还需要寻找有没有更好的技巧能够避免工厂存在的缺陷。
  • 代码:https://gitee.com/dongxianghd/DesignMode/tree/master/FactoryMode

[ 注1]  一个方法返回不同的实例。

[ 注2]  C++继承中可以将基类虚函数的重写,基类类型会调用子类重写的虚函数。

[ 注3]  开闭原则规定“软件中的对象(类,模块,函数等等)应该对于扩展是开放的,但是对于修改是封闭的”,这意味着一个实体是允许在不改变它的源代码的前提下变更它的行为。

相关文章
|
6月前
|
设计模式 消息中间件 监控
并发设计模式实战系列(5):生产者/消费者
🌟 ​大家好,我是摘星!​ 🌟今天为大家带来的是并发设计模式实战系列,第五章,废话不多说直接开始~
222 1
|
6月前
|
设计模式 监控 Java
并发设计模式实战系列(8):Active Object
🌟 大家好,我是摘星!🌟今天为大家带来的是并发设计模式实战系列,第8章,废话不多说直接开始~
131 0
并发设计模式实战系列(8):Active Object
|
2月前
|
设计模式 人工智能 算法
基于多设计模式的状态扭转设计:策略模式与责任链模式的实战应用
接下来,我会结合实战案例,聊聊如何用「策略模式 + 责任链模式」构建灵活可扩展的状态引擎,让抽奖系统的状态管理从「混乱战场」变成「有序流水线」。
|
6月前
|
设计模式 负载均衡 监控
并发设计模式实战系列(2):领导者/追随者模式
🌟 ​大家好,我是摘星!​ 🌟今天为大家带来的是并发设计模式实战系列,第二章领导者/追随者(Leader/Followers)模式,废话不多说直接开始~
202 0
|
6月前
|
设计模式 监控 Java
并发设计模式实战系列(1):半同步/半异步模式
🌟 ​大家好,我是摘星!​ 🌟今天为大家带来的是并发设计模式实战系列,第一章半同步/半异步(Half-Sync/Half-Async)模式,废话不多说直接开始~
189 0
|
6月前
|
设计模式 运维 监控
并发设计模式实战系列(4):线程池
需要建立持续的性能剖析(Profiling)和调优机制。通过以上十二个维度的系统化扩展,构建了一个从。设置合理队列容量/拒绝策略。动态扩容/优化任务处理速度。检查线程栈定位热点代码。调整最大用户进程数限制。CPU占用率100%
442 0
|
6月前
|
设计模式 消息中间件 监控
并发设计模式实战系列(3):工作队列
🌟 ​大家好,我是摘星!​ 🌟今天为大家带来的是并发设计模式实战系列,第三章,废话不多说直接开始~
183 0
|
6月前
|
设计模式 存储 安全
并发设计模式实战系列(7):Thread Local Storage (TLS)
🌟 大家好,我是摘星! 🌟今天为大家带来的是并发设计模式实战系列,第七章Thread Local Storage (TLS),废话不多说直接开始~
225 0
|
6月前
|
消息中间件 设计模式 监控
并发设计模式实战系列(9):消息传递(Message Passing)
🌟 大家好,我是摘星!🌟今天为大家带来的是并发设计模式实战系列,第九章,废话不多说直接开始~
124 0
|
6月前
|
设计模式 安全 NoSQL
并发设计模式实战系列(10):Balking(犹豫模式)
🌟 大家好,我是摘星!🌟今天为大家带来的是并发设计模式实战系列,第10章,废话不多说直接开始~
64 0
下一篇
oss云网关配置