
文档技术博客教程【免费下载链接】weekly前端精读周刊。帮你理解最前沿、实用的技术。项目地址https://gitcode.com/GitHub_Trending/we/weekly点击查看免费下载Abstract Factory抽象工厂是创建型模式中抽象程度最高的一档其核心意图是“提供一个接口以创建一系列相关或相互依赖的对象而无须指定它们具体的类”。本文以《前端精读周刊》第 167 期《设计模式 - Abstract Factory 抽象工厂》 为骨架结合本仓库工厂方法篇与可视化搭建系列的源码级实践讲清抽象工厂的意图、结构、代码实现与适用边界读完即可判断“何时该用抽象工厂、何时该避而远之”。一、定位创建型模式的抽象阶梯工厂类模式的抽象程度从低到高分为三级简单工厂模式用一个工厂类根据入参switch出不同产品新增产品需要修改已有类不符合开闭原则工厂模式Factory Method定义创建对象的接口Creator把实例化动作延迟到子类ConcreteCreator去完成新增产品只需新增子类抽象工厂模式Abstract Factory不再关心“创建单个对象”而是通过一个接口创建一整组彼此相关、相互依赖的对象家族调用方只描述这组对象之间的组合关系完全不关心具体是哪家实现。抽象工厂是站在工厂方法之上的进一步抽象工厂方法解决“一个对象怎么造”抽象工厂解决“一组对象怎么配套着造”。原文档也明确指出“抽象工厂使用了工厂方法”两者是递进而非并列关系——抽象工厂的每个ConcreteFactory内部本质就是在调用工厂方法来逐个生产产品。二、意图解读三个例子看懂“相关或相互依赖的对象”原文档用三个日常例子帮助我们建立直觉这里逐一还原并点出其共性问题。2.1 汽车工厂只认“宝马”这个品牌不认零部件品牌汽车由大量零部件组成消费者不会去轮胎工厂、方向盘工厂、车窗工厂逐个采购而是把需求整体交给宝马工厂抽象工厂组装。作为工厂老板汽车由哪些部件构成是固定的只是不同配件有不同型号、来自不同厂商你需要推出几款不同组合的车型匹配不同价位消费者。这里“固定的”是部件之间的组合关系一辆车必须有一组配套的方向盘 轮胎 车窗可拓展的是每一类部件的具体实现普通方向盘、运动方向盘、高级真皮方向盘……。2.2 迷宫游戏生成算法不感知“新材料”迷宫由房间、门、墙三种元素组成组合关系固定房间之间用门连接、四周有墙生成算法只调用“房间工厂、门工厂、墙工厂”产出实例。新资料片要加入可回复体力的房间、需要魔法钥匙的门、可被炸弹破坏的墙。如果不修改已有迷宫生成算法遵守开闭原则就希望算法完全感知不到新材料的存在这正是抽象工厂要解决的问题。2.3 事件联动搭建引擎中的“组件家族”前端搭建引擎要做一套关联机制点击表格组件单元格弹出模态框内部展示折线图。业务方有定制表格、模态框、折线图的需求但三者之间的联动关系是确定的。表格可以被替换为任意业务方注册的表格只要满足点击onClick机制即可折线图既可以用Echarts画也可以用G2画联动机制本身只描述“点击表格单元格 → 弹出模态框展示折线图”这个关系不关心组件具体是谁实现的。2.4 三个例子的共同结构例子固定不变的关系可拓展的具体实现汽车工厂汽车的部件组合关系方向盘、轮胎、车窗的具体品牌型号迷宫游戏房间、门、墙的组合生成算法普通素材 / 魔法素材 / 熔岩素材事件联动表格→模态框→折线图的联动机制业务方注册的任意表格、模态框、折线图实现意图提供一个接口以创建一系列相关或相互依赖的对象而无须指定它们具体的类。三组例子都吻合我们要创建的是一系列相关或相互依赖的对象汽车的配件组、迷宫的素材组、联动的组件组同时无须指定它们具体的类——不关心方向盘是什么牌子、房间是不是普通房间、折线图是不是用Echarts画的只要描述好它们之间的关系即可。这带来的核心收益是未来拓展新的方向盘、新的房间、新的折线图时不需要修改抽象工厂本身。三、结构图拆解四个角色的职责划分原文档给出的类图可以拆成四个角色各自职责如下AbstractFactory抽象工厂描述创建产品的抽象关系。比如“迷宫如何生成”“表格和趋势图怎么联动”。它不实例化任何具体产品只声明“我要用一组产品搭出一个东西”的流程。ConcreteFactory具体工厂实现具体用哪套产品。可以有多个具体工厂比如ConcreteFactory1实例化的墙壁是普通墙壁ConcreteFactory2实例化的墙壁是魔法墙壁。但二者对AbstractFactory暴露的接口完全一致所以AbstractFactory不需要关心当前调用的是哪一个具体工厂。AbstractProduct产品抽象类描述某类产品的创建/使用方式比如方向盘、墙壁、折线图都有统一的抽象接口。ConcreteProduct具体产品具体实现。比如ConcreteProduct1用canvas画表格、用G2画折线图ConcreteProduct2用div画表格、用Echarts画折线图。拓展的逻辑因此变得非常干净当我们要新增一套“用Echarts画的折线图 用svg画的表格 用div画的模态框”组成的事件机制时只需要再创建一个ConcreteFactory3做对应实现把它传给AbstractFactory即可完全不需要修改AbstractFactory方法本身。四、代码实现一个可运行的 TypeScript 骨架原文档给出了用 JavaScript 编写的核心骨架这里保留并扩充注释使其可复制、可运行、可对照三个例子理解// 抽象工厂只描述“一组产品如何被组合”不关心具体实现 class AbstractFactory { createProducts(concreteFactory: ConcreteFactory) { // 从具体工厂拿到两个产品产品族中的成员 const productA concreteFactory.createProductA(); const productB concreteFactory.createProductB(); // 建立 A 与 B 固定的关联 // 即便 A 与 B 的实现换成任意实现这条关联逻辑都不受影响 productA.bind(productB); } }productA.bind(productB)是一种抽象表示映射到三个例子分别是汽车工厂的例子组装汽车的过程迷宫游戏的例子生成迷宫的过程事件联动的例子创建组件间关联的过程。也就是说AbstractFactory承载的是“把产品族绑定到一起”的固定流程而ConcreteFactory决定这个流程具体使用的产品实现。假设迷宫有两套素材普通素材与魔法素材。只要分别创建普通素材工厂ConcreteFactoryA与魔法素材工厂ConcreteFactoryB调用createProducts时传入普通素材产出的就是普通素材搭建的迷宫传入魔法素材产出的就是魔法素材搭建的迷宫// 普通素材迷宫 abstractFactory.createProducts(new ConcreteFactoryA()); // 魔法素材迷宫 abstractFactory.createProducts(new ConcreteFactoryB());当我们要创建一套新迷宫材料熔岩迷宫时只需创建一套熔岩素材熔岩房间、熔岩门、熔岩墙壁再组装一个ConcreteFactoryC熔岩素材生成工厂传递给AbstractFactory.createProducts// 新增熔岩素材工厂无需修改 AbstractFactory 一行代码 class ConcreteFactoryC implements ConcreteFactory { createProductA() { return new LavaRoom(); } createProductB() { return new LavaDoor(); } } abstractFactory.createProducts(new ConcreteFactoryC());可以发现使用抽象工厂模式我们可以轻松拓展新的素材——新汽车配件、新迷宫素材、新事件联动组件这个过程只需要新建类不需要修改任何已有类符合开闭原则。五、仓库印证从工厂方法到可视化搭建引擎的“组件家族”本仓库中与抽象工厂最直接相关的证据有两处可作为加深理解的佐证。5.1 工厂方法篇抽象工厂内含工厂方法《设计模式 - Factory Method 工厂方法》 指出“你也许也发现了这就是抽象工厂中其中的一步所以抽象工厂使用了工厂方法。” 也就是说抽象工厂里每个ConcreteFactory.createProductX()的单个产品生产本身就是一个工厂方法的调用。两者是“策略与战术”的关系抽象工厂负责产品族的配套组合战略层工厂方法负责单个产品的实例化战术层。5.2 可视化搭建系列事件联动例子的真实落地原文档“事件联动”的例子在本仓库的可视化搭建系列中被完整落地构成一个活生生的抽象工厂式设计《组件注册与画布渲染》 定义了Designer componentMetas{[]} componentTree{} /的 APIcomponentMetas是组件元信息数组componentTree是组件树。画布渲染只认“组件名 props children”这套抽象接口具体组件由业务方注册——这正是“只描述关系、不指定具体类”。《组件值与联动》 定义componentMeta.valueRelates声明式描述组件间的联动关系sourceComponentId → targetComponentId组件实例通过selector(({ relates }) relates)消费联动状态。联动机制本身不关心表格是不是canvas画的、折线图是不是Echarts画的只要组件满足onClick等约定接口即可——这与“事件联动”例子的抽象完全一致。《定义联动协议》 更进一步把“组件实例的linkage属性”这种业务协议通过valueRelates转化为底层值联动关系用runtimeProps selector将联动结果注入组件props。组件实例定义的是相对关系$self、$deps[0]框架层转化为以componentId定位的绝对关系统一处理。整个设计呈现清晰的“框架层固定协议骨架 业务层自由注册实现”的分层与抽象工厂“抽象关系固定、具体实现可插拔”的思想一脉相承。从源码结构可以推断该搭建引擎之所以能“接入任意业务组件而不需要组件库做任何适配”组件注册与画布渲染 中提到注入 Antd 等框架组件不改一行源码即可接入正是因为它把“组件树结构描述”与“组件实现”彻底分离本质上是抽象工厂思想的工程化应用。六、弊端新增“产品种类”时开闭原则失效任何设计模式都有适用场景抽象工厂也不例外。原文档用同一个例子反证了它的痛点如果需求不是拓展“新实现”而是新增元素种类汽车工厂要给汽车加一个新部件自动驾驶系统迷宫游戏要新增一个功能素材陷阱事件联动要新增一个联动对象明细趋势统计表格。这种情况会非常复杂因为要为所有ConcreteFactory新增每一个新元素——如果已有素材包 1999就需要同时增加 999 个对应的陷阱实现普通陷阱、魔法陷阱、熔岩陷阱……工作量线性爆炸还要修改抽象工厂把新元素与旧元素之间重新建立关联——这直接违背了开闭原则。根本原因在于抽象工厂做的是“把已有的房间、门、墙壁建立关联”因为操作的是抽象类所以拓展一套具体实现熔岩素材包对它没有感知、很容易但新增产品种类陷阱时抽象工厂必须把陷阱与前三者重新建立关联修改是绕不开的。七、总结与决策表抽象工厂的适用边界可以用一句话概括抽象工厂对“新增已有产品的实现”适用对“新增一个产品种类”不适用。变更类型举例是否适合抽象工厂原因增加一种产品风格/实现新增熔岩素材包、新增运动方向盘、新增 G2 折线图实现✅ 适合只是新增一个ConcreteFactory抽象关系不动符合开闭原则增加一个产品种类新增陷阱、新增自动驾驶系统、新增联动对象❌ 不适合需要修改抽象工厂重建关联还要为每个具体工厂补实现违背开闭原则因此只有“产品种类稳定、需要频繁拓展产品风格”的场景才适合使用抽象工厂设计模式。在真实项目中请先审视自己的需求属于哪一类变更再决定是否引入这套模式——这也印证了设计模式选型的第一原则为合适的场景选择一种能将优势发扬光大、以至于能掩盖弊端的模式。延伸阅读本篇文章出处设计模式/167.精读《设计模式 - Abstract Factory 抽象工厂》.md递进关系设计模式/169.精读《设计模式 - Factory Method 工厂方法》.md搭建引擎中的落地可视化搭建/268.如何抽象可视化搭建.md、可视化搭建/269.组件注册与画布渲染.md、可视化搭建/273.组件值与联动.md、可视化搭建/274.定义联动协议.md本仓库完整设计模式系列入口见 readme.md赞分享文档技术博客教程【免费下载链接】weekly前端精读周刊。帮你理解最前沿、实用的技术。项目地址https://gitcode.com/GitHub_Trending/we/weekly点击查看免费下载相关推荐DesignPatternsPHP 抽象工厂模式Abstract Factory实战指南用 WriterFactory 在 PHP 8.x 中按平台创建系列相关对象DesignPatternsPHP 抽象工厂模式Abstract Factory实战指南用 WriterFactory 在 PHP 8.x 中按平台创建系示例工程教程Polybar抽象工厂相关对象家族的创建终极指南Polybar抽象工厂相关对象家族的创建终极指南 Polybar是一个高度可定制的Linux状态栏工具通过其强大的抽象工厂模式实现了模块化设计。这个设计模式桌面应用Java 抽象工厂模式Abstract Factory实战指南以 java-design-patterns 王国示例掌握对象家族创建Java 抽象工厂模式Abstract Factory实战指南以 java design patterns 王国示例掌握对象家族创建 抽象工厂Abstr示例工程教程上一篇opencode-anthropic-authOpenCode 的 Anthropic 登录插件的 GitHub Issues 使用指南提问与报 Bug 的正确姿势下一篇GTTools基于Android的性能指标采集与监控工具包创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考