设计模式学习[13]---抽象工厂模式+简单工厂+工厂方法模式回顾

ops/2024/12/24 3:35:29/

文章目录

  • 前言
    • 1.原理阐述
      • 1.1 说明1
      • 1.2 说明2
    • 2.举例
  • 总结

前言

之前写过一些工厂的相关内容,详情见这两篇:简单工厂与工厂方法

这篇博客主要讲抽象工厂模式

1.原理阐述

1.1 说明1

抽象工厂模式:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。

直接看概念其实还是有点懵的,这里翻译一下。
用一个抽象的类,这个类叫做抽象工厂,去封装一系列相互依赖的实例化的过程。这个实例化涉及到互相依赖的接口,比如A里面包含了abc类型,B里面也包含了abc类型,创建a类型,其实需要依赖它是A还是B这种类型。这样讲可能还是不懂。


1.2 说明2

所以我又结合了另一本设计模式的书看,下面是另一种说法(例子来自于另一本书)。

如果一个工厂子类能够生产不止一种具有相同规格规则的怪物对象,那么就可以有效地减少所创建的工厂子类数量,这就是抽象工厂模式的核心思想

这里怪物对象的例子如下:
假如现在有三种怪物,亡灵类,元素类和机械类;还有三种战斗场地:沼泽地区、山脉地区、城镇。
那么这样的情况,整个游戏其实有9类怪物,分别是沼泽地区的三类怪物,山脉地区的三类怪物、城镇地区的三类怪物。
这样划分的是因为,每个区域的同类型怪物能力差距很大,比如沼泽里面的亡灵类怪物攻击力比城镇的亡灵类怪物攻击力高,这其实就是打游戏的时候的一个环境加成吧。

这时候需要提及了两个概念。产品等级结构产品族
将刚才提到的9种怪物放到一个坐标轴中,我们会发现,按照行的方式来看,每个怪物的种类不同,但是都位于相同场景;如果按照列的方式来看,怪物的种类都相同,但是位于不同场景。

所以如果用一个工厂子类生产一个产品族(1行),那么因为有3个产品族,所以只需要3个工厂可以生产9个产品。

说到最后,就是通过抽象工厂模式,把一个复杂的多种情况的对象,用一个简单的类别工厂作分割。对于上面的例子,把3×3的矩阵变成3个1*3的矩阵。这样每个1×3的矩阵就是一个小的工厂。回到最外层,整个创建实例的最外层的那个接口,就是抽象工厂模式的一种体现。

是不是还是晕?是的,我写到这里,感觉还没能够用非常通俗的语言说明。
我们再来看例子。

2.举例

场景:怪兽有三种类型,亡灵类、元素类、机械类。

需求:我现在要创建不同类型的怪兽对象

基本做法:创建一个怪兽类,怪兽有个属性type表示他是什么类型,要创建哪种对象就根据type类型创建具体的怪兽类,这时候用户需要知道有哪些对象,同时如果还想再加怪兽种类的话,需要在Monster类中进行拓展,类似于switch分支。每加一个类型的怪兽,就需要修改Monster类中创建子类的switch分支。

简单工厂做法:
基本做法中,这种不断修改switch分支的方式,违反了开闭原则,在软件设计中是不合理的。接下来用简单工厂模式进行优化。
我们通过创建一个MonsterFactory工厂,把根据类型实例化过程交给这个工厂类去做,那么对于Monster类来说,它是保持封闭的。如果新增加一个特殊的怪兽类,只需要让这个特殊怪兽类去继承Monster类即可,修改是在这个MonsterFactory这个抽象类中。
下面是简单工厂类图。
在这里插入图片描述

工厂方法模式:
上面这个类图中,我们其实是把这个对类的修改从Monster类移到了MonsterFactory中,但实际上对于MonsterFactory类来说,他也不满足开闭原则,因为我们要一直改这个MonsterFactory类中的switch分支来创建对象。

那么如果我们把这个switch分支用类的方式进行脱离呢?那就是工厂方法模式了。

我们对每一个特定类型的怪兽用一个工厂类去实例化,比如对于亡灵类M_Undead,我们用M_UndeadFactory工厂类去实例化它,其他的几个类也一样。
这样我们可以根据类型string字段type,去创建对应的工厂类,再具体的去调用工厂类的createMonster()函数,从而完成具体实例的创建

在这里插入图片描述

抽象工厂模式
对于我们一开始提及的需求我现在要创建不同类型的怪兽对象,那如果新增一个环境条件呢?就是我们在说明2里面提到的,沼泽,城镇,山脉地形。
那如果按照我们工厂方法模式,是不是得要创建类似于下面9个工厂类:

沼泽亡灵族工厂类
沼泽元素族工厂类
沼泽机械族工厂类
山脉亡灵族工厂类
山脉元素族工厂类
山脉机械族工厂类
城镇亡灵族工厂类
城镇元素族工厂类
城镇机械族工厂类

类图的话就是类似下面红框,不断的去继承这个M_ParFactory

在这里插入图片描述

这似乎工厂太多了吧?所以我们再回到说明2来看,引入产品等级结构产品族

这时候我们再把UML类图画一下,如下图所示。

在这里插入图片描述

这个类图中,我们创建沼泽、山脉、城镇类型的怪兽生成工厂,让一个工厂子类能够生产不止一种具有相同规格规则的怪物对象,那么就可以有效地减少所创建的工厂子类数量。

那么具体main函数的代码体现如下:

//这里省略各种类的说明,具体参考UML类图M_ParFactory* p_mou_fy=new M_Factory_Mountain();//多态工厂,山脉地区的工厂
Monster* pM1=p_mou_fy->createMonster_Element();//创建山脉地区的元素类怪物M_ParFactory* p_twn_fy=new M_Factory_Town();//多态工厂,城镇的工厂
Monster* pM2=p_twn_fy->createMonster_Undead();//创建城镇的亡灵类怪物
Monster* pM3=p_twn_fy->createMonster_Mechanic();//创建城镇的机械类怪物

在main代码中,我们把所有的怪物的创建通过一个M_ParFactory工厂指针去创建,需要什么地区的就new一个什么地区的对象,需要什么类型的怪物,就用Monster*指针去指向。
我们实际对外暴露,并给用户去使用的类实际上就是一个创建怪物的工厂指针,还有一个怪物类的指针。

写到这,应该能明白抽象工厂的意义了。减少工厂方法模式的工厂数量,减少对外暴露的接口,把细节的东西全部封装到一个接口里面。同时要满足开闭原则。

总结

工厂这个大模式的意义最主要还是在于做到软件开发过程中的开闭原则,三种工厂一般作用 的范围不太一样,下面说一下三者的区分和总结。

从代码复杂度上:简单工厂模式最简单,工厂方法次之,抽象工厂模式最复杂。把简单工厂模式中的代码修改得符合开闭原则,就变成了工厂方法模式,修改工厂方法模式的代码使得一个工厂支持对多个具体产品的生产,就变成了抽象工厂模式
从需要的工厂数量上:简单工厂模式需要的工厂数量最少,工厂方法模式需要的工厂数量最多,抽象工厂模式能够有效地减少工厂方法模式所需要的工厂数量(可以将工厂方法模式看做抽象工厂模式的一种特例——抽象工厂模式中的工作若只创建一种对象就是工厂方法模式)
从实际应用上:当项目中的产品数量比较少时考虑使用简单工厂模式,如果项目稍大一点或者为了满足开闭原则,则可以使用工厂方法模式,而对于大型项目中有众多厂商并且每个厂商都生产一系列产品时应该考虑使用抽象工厂模式


http://www.ppmy.cn/ops/144461.html

相关文章

鱼跃医疗获评2024年国家级“绿色工厂”,以绿色制造树立行业标杆

近日,工业和信息化部公布了2024年度绿色制造名单,鱼跃医疗凭借在绿色制造和可持续发展方面的卓越表现,成功入选并获评国家级“绿色工厂”。 “绿色工厂”是工信部为贯彻落实国家《工业绿色发展规划》,加快推动绿色制造体系建设&a…

在 Linux 系统中,让 apt 使用 HTTP 代理

在 Linux 系统中,要让 apt 使用 HTTP 代理,有几种方法可以实现: ### 1. 临时设置代理 你可以通过设置环境变量来临时为 apt 命令设置代理。这种方法不需要修改任何配置文件,只需在命令行中设置环境变量即可。例如: …

【Java入门指南 Day11:Lambda表达式与Stream API】

一、Lambda表达式基础 Lambda表达式是Java 8引入的一个重要特性,它让我们可以将行为像数据一样传递。可以把它理解为一种简洁的、匿名的函数定义方式。 Lambda表达式语法 // 基本语法: (参数) -> {表达式}// 1. 无参数 Runnable r () -> System.out.print…

单片机与MQTT协议

MQTT 协议简述 MQTT(Message Queuing Telemetry Transport,消息队列遥测传输协议),是一种基于发布 / 订阅(publish/subscribe)模式的 “轻量级” 通讯协议,该协议构建于 TCP/IP 协议上&#xf…

全面解析 Kubernetes 流量负载均衡:iptables 与 IPVS 模式

目录 Kubernetes 中 Service 的流量负载均衡模式 1. iptables 模式 工作原理 数据路径 优点 缺点 适用场景 2. IPVS 模式 工作原理 数据路径 优点 缺点 适用场景 两种模式的对比 如何切换模式 启用 IPVS 模式 验证模式 总结 Kubernetes 中 Service 的流量负载…

webpack如何自定义插件?示例

在Webpack中创建自定义插件通常涉及以下步骤: 使用 module.exports 导出一个类或者一个函数。 这个类或者函数需要实现 apply 方法,这个方法会接收一个 compiler 对象作为参数。 在 apply 方法中,你可以订阅Webpack的生命周期钩子&#xff…

C 文件读写

C 文件读写 文件读写是编程中非常基础且重要的功能,特别是在C语言中。C语言提供了一套标准的库函数,用于文件的打开、读取、写入和关闭等操作。本文将详细介绍如何在C语言中进行文件读写操作。 1. 文件的概念 在C语言中,文件通常被视为一个字符序列。每个文件都有一个与之…

Type 类型 总结

GetType、Typeof Type 官网资料 IsAssignableFrom IsAssignableTo 在C#中&#xff0c;Type.IsAssignableFrom方法用于判断一个类型是否可以从另一个类型赋值。它检查源类型是否是目标类型的基类或接口。 isAssignableFrom(Class<?> c) 标识 “当前Class 是否是给定…