Abstract Factory(形象工厂)属于创立型模式,工厂类模式形象水平从低到高分为:简略工厂模式 -> 工厂模式 -> 形象工厂模式。
用意:提供一个接口以创立一系列相干或相互依赖的对象,而无须指定它们具体的类。
举例子
如果看不懂下面的用意介绍,没有关系,设计模式须要在日常工作里用起来,联合例子能够加深你的了解,上面我筹备了三个例子,让你领会什么场景下会用到这种设计模式。
汽车工厂
咱们都晓得汽车有很多零部件,随着工业革命带来的分工,很多整机都能够被轻松替换。但理论生存中咱们消费者不违心这样,咱们心愿买来的宝马车所蕴含的零部件都是同一系列的,以保障最大的匹配度,从而带来更好的性能与舒适度。
所以消费者不违心到轮胎工厂、方向盘工厂、车窗工厂去一个个洽购,而是将需要提给了宝马工厂这家形象工厂,由这家工厂负责组装。那你是这家工厂的老板,已知汽车的组成部件是固定的,只是不同配件有不同的型号,别离来自不同的制造厂商,你须要推出几款不同组合的车型来满足不同价位的消费者,你会怎么设计?
迷宫游戏
你做一款迷宫游戏,已知元素有房间、门、墙,他们之间的组合关系是固定的,你通过一套算法生成随机迷宫,这套算法调用房间、门、墙的工厂生成对应的实例。但随着新资料片的放出,你须要生成具备新性能的房间(能够回复膂力)、新性能的门(须要魔法钥匙能力关上)、新性能的墙(能够被炸弹毁坏),但批改已有的迷宫生成算法违反了开闭准则(须要在已有对象进行批改),如果你心愿生成迷宫的算法齐全不感知新资料的存在,你会怎么设计?
事件联动
假如咱们做一个前端搭建引擎,当初心愿做一套关联机制,以实现点击表格组件单元格,能够弹出一个模态框,外部展现一个折线图。已知业务方存在定制表格组件、模态框组件、折线图组件的需要,但组件之间联动关系是确定的,你会怎么设计?
用意解释
在汽车工厂的例子中,咱们已知车子的形成部件,为了组装成一辆车子,须要以肯定形式拼装部件,而具体用什么部件是须要可拓展的。
在迷宫游戏的例子中,咱们已知迷宫的组成部分是房间、门、墙,为了生成一个迷宫,须要以某种算法生成许多房间、门、墙的实例,而具体用哪种房间、哪种门、哪种墙是这个算法不关怀的,是须要可被拓展的。
在事件联动的例子中,咱们已知这个表格弹出趋势图的交互场景根本组成元素是表格组件、模态框组件、折线图组件,须要以某种联动机制让这三者间产生联动关系,而具体是什么表格、什么模态框组件、什么折线图组件是这个事件联动所不关怀的,是须要能够被拓展的,表格能够被替换为任意业务方注册的表格,只有满足点击 onClick
机制就能够。
用意:提供一个接口以创立一系列相干或相互依赖的对象,而无须指定它们具体的类。
这三个例子不正是合乎下面的用意吗?咱们要设计的形象工厂就是要 创立一系列相干或相互依赖的对象 ,在下面的例子中别离是汽车的组成配件、迷宫游戏的素材、事件联动的组件。 而无须指定它们具体的类 ,也就阐明了咱们不关怀车子方向盘用的是什么牌子,迷宫的房间是不是一般房间,联动机制的折线图是不是用 Echarts
画的,咱们只有形容好他们之间的关系即可, 这带来的益处是,将来咱们拓展新的方向盘、新的房间、新的折线图时,不须要批改形象工厂。
结构图
AbstractFactory
就是咱们要的形象工厂,形容了创立产品的形象关系,比方形容迷宫如何生成,表格和趋势图怎么联动。
至于具体用什么方向盘、用什么房间,是由 ConcreteFactory
实现的,所以咱们可能有多个 ConcreteFactory
,比方 ConcreteFactory1
实例化的墙壁是一般墙壁,ConcreteFactory2
实例化的墙壁是魔法墙壁,但其对 AbstractFactory
的接口是统一的,所以 AbstractFactory
不须要关怀具体调用的是哪一个工厂。
AbstractProduct
是产品抽象类,形容了比方方向盘、墙壁、折线图的创立办法,而 ConcreteProduct
是具体实现产品的办法,比方 ConcreteProduct1
创立的表格是用 canvas
画的,折线图是用 G2
画的,而 ConcreteProduct2
创立的表格是用 div
画的,折线图是用 Echarts
画的。
这样,当咱们要拓展一个用 Rcharts
画的折线图,用 svg
画的表格,用 div
画的模态框组成的事件机制时,只须要再创立一个 ConcreteFactory3
做相应的实现即可,再将这个 ConcreteFactory3
传递给 AbstractFactory
,并不需要批改 AbstractFactory
办法自身。
代码例子
上面例子应用 javascript 编写。
class AbstractFactory {createProducts(concreteFactory: ConcreteFactory) {const productA = concreteFactory.createProductA();
const productB = concreteFactory.createProductB();
// 建设 A 与 B 固定的关联,即使 A 与 B 实现换成任意实现都不受影响
productA.bind(productB);
}
}
productA.bind(productB)
是一种形象示意:
- 对于汽车工厂的例子,示意组装汽车的过程。
- 对于迷宫游戏的例子,示意生成迷宫的过程。
- 对于事件联动的例子,示意创立组件间关联的过程。
假如咱们的迷宫有两套素材,别离是一般素材与魔法素材,只有在别离创立一般素材工厂 ConcreteFactoryA
,与魔法素材工厂 ConcreteFactoryB
,调用 createProducts
时传入的是一般素材,则产出的就是一般素材搭建的迷宫,传入的是魔法素材,则产出的就是用魔法素材搭建的迷宫。
当咱们要创立一套新迷宫资料,比方熔岩迷宫,咱们只有创立一套熔岩素材(熔岩房间、熔岩门、熔岩墙壁),再组装一个 ConcreteFactoryC
熔岩素材生成工厂传递给 AbstractFactory.createProducts
即可。
咱们能够发现,应用形象工厂模式,咱们能够轻松拓展新的素材,比方拓展一套新的汽车配件,拓展一套新的迷宫素材,拓展一套新的事件联动组件,这个过程只须要新建类即可,不须要批改任何类,合乎开闭准则。
弊病
任何设计模式都有其实用场景,反过来也阐明了在某些场景下不实用。
还是下面的例子,如果咱们的需要不是拓展一个新轮子、新墙壁、新折线图,而是:
- 汽车工厂要给汽车加一个新部件:主动驾驶零碎。
- 迷宫游戏要新增一个性能素材:陷阱。
- 事件联动要新增一个联动对象:明细趋势统计表格。
你看,这种状况不是为已有元素新增一套实现,而是实现一些新元素,就会非常复杂,因为咱们不仅要为所有 ConcreteFactory
新增每一个元素,还要批改形象工厂,以将新元素与旧元素间建立联系,违反了开闭准则。
因而,对于已有元素固定的零碎,适宜应用形象工厂,反之不然。
总结
形象工厂对新增已有产品的实现实用,对新增一个产品种类不实用,能够参考联合了例子的下图加深了解:
拓展一个熔岩素材包是 减少一种产品格调 ,适宜应用形象工厂设计模式;拓展一个陷阱是 减少一个产品种类,不适宜应用形象工厂设计模式。为什么呢?看下图:
创立迷宫这个形象工厂做的事件,是把已有的房间、门、墙壁建设关联,因为操作的是抽象类,所以拓展一套具体实现(熔岩素材包)对这个形象工厂没有感知,这样做很容易。
但如果新增一个产品种类 – 陷阱,能够看到,形象工厂必须将陷阱与前三者从新建设关联,这就要批改形象工厂,不合乎开闭准则。同时,如果咱们已有素材包 1 ~素材包 999,就须要同时减少 999 个对应的陷阱实现(一般陷阱、魔法陷阱、熔岩陷阱),其工作量会十分大。
因而,只有产品种类稳固时,须要频繁拓展产品格调时才适宜用形象工厂设计模式。
探讨地址是:精读《设计模式 – Abstract Factory 形象工厂》· Issue #271 · dt-fe/weekly
如果你想参加探讨,请 点击这里,每周都有新的主题,周末或周一公布。前端精读 – 帮你筛选靠谱的内容。
关注 前端精读微信公众号
版权申明:自在转载 - 非商用 - 非衍生 - 放弃署名(创意共享 3.0 许可证)