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

文章目录

前言

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

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

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函数的代码体现如下:

cpp 复制代码
//这里省略各种类的说明,具体参考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*指针去指向。

我们实际对外暴露,并给用户去使用的类实际上就是一个创建怪物的工厂指针,还有一个怪物类的指针。

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

总结

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

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

相关推荐
码到成龚44 分钟前
SQL server学习08-使用索引和视图优化查询
数据库·学习
牛哄哄的柯南2 小时前
有监督学习 vs 无监督学习:机器学习的两大支柱
人工智能·学习·机器学习
等什么君!2 小时前
学习反射(反射的使用,反射的应用场景)
java·开发语言·学习
逸琅2 小时前
五十个网络安全学习项目——(九)无线网络安全分析
网络·学习·web安全
右恩2 小时前
【二维码美化】
服务器·前端·python·学习
夏旭泽3 小时前
设计模式-中介模式
设计模式
小仙有礼了3 小时前
Arcgis for javascript 开发学习经验
javascript·学习·arcgis
越甲八千4 小时前
重拾设计模式--原型模式
设计模式·原型模式
阿佳举世无双4 小时前
设计模式-访问者设计模式
设计模式