发布时间:2026/8/4 4:08:01
设计模式 04 · 抽象工厂模式 上一篇的工厂方法,解决的是一个产品有多种实现,该造哪一个。但现实里有一类更麻烦的情况:你要造的不是一个产品,而是一整套互相搭配、必须配套使用的产品。比如做一笔线上订单,你需要的不只是一个Order,还有配套的电子发票Invoice、以及虚拟发货单Shipment;而换成门店订单,同样是这三样,但每一样都得换成门店专用的版本——纸质发票、到店自提单。这三样东西必须成套出现,不能混搭:你不能给一笔线上订单配一张需要邮寄的纸质发票。这种成套、配套、不能混搭的产品组合,就是抽象工厂模式(Abstract Factory)的主场。它是工厂三兄弟里最重、也最容易被误用的一个。这一篇我们把它讲清楚,尤其要讲透它一个非常独特、也最需要权衡的特性——开闭原则在它身上是倾斜的:某个方向的扩展轻松无比,另一个方向的扩展却要伤筋动骨。理解了这个倾斜性,你才算真懂了抽象工厂,也才知道它到底适不适合你的场景。这篇文章按这条线索展开:先说清楚产品族这个核心概念,它和上一篇的产品等级有什么区别;再看如果硬用工厂方法去解决产品族问题会碰到什么麻烦,从而引出抽象工厂;然后讲清它的角色、骨架和那张关键的网格图;接着重点剖析它的开闭倾斜性——为什么加一个新渠道很爽、加一个新单据却很痛;最后给出选型建议、现实身影,以及工厂三兄弟的总对比。全程用线上/门店两种渠道 × 订单/发票/发货单三种单据这个例子。目录两个维度:产品族与产品等级硬用工厂方法会怎样:产品族的困境抽象工厂登场:一个工厂造一整族角色、骨架与那张网格图最关键的取舍:开闭原则的倾斜性什么时候该用,什么时候别碰现实身影,与工厂三兄弟总对比一、两个维度:产品族与产品等级要理解抽象工厂,必须先建立一个二维的视角。上一篇的工厂方法,本质上只有一个维度:一堆同类产品(各种Payment)的不同实现。而抽象工厂面对的是两个维度交织的情况。用我们的例子来说。一笔订单业务涉及三种不同类型的单据:订单Order发票Invoice发货单Shipment同时,整个系统又有两个不同的渠道:线上渠道:线上订单、电子发票、虚拟发货单门店渠道:门店订单、纸质发票、到店自提单把这两个维度画成一张表,一切就清楚了:订单 Order发票 Invoice发货单 Shipment线上渠道OnlineOrderElectronicInvoiceVirtualShipment门店渠道StoreOrderPaperInvoicePickupShipment这里有两个关键术语,务必分清:产品等级结构(Product Hierarchy):表格的每一列。比如发票这一列,ElectronicInvoice和PaperInvoice都是发票,是同一种产品的不同实现——这正是上一篇工厂方法管的那个维度。产品族(Product Family):表格的每一行。比如线上渠道这一行,OnlineOrderElectronicInvoiceVirtualShipment这三样,虽然类型不同,但同属线上渠道、必须搭配使用,它们组成一个产品族。一句话记住这个区别:产品等级是同一种东西的不同牌子,产品族是同一个品牌下的整套不同东西。工厂方法关心的是列(一种产品选哪个实现),抽象工厂关心的是行(一整族产品成套地造出来)。这就是两者最本质的分界。二、硬用工厂方法会怎样:产品族的困境假设我们不用抽象工厂,而是用上一篇的工厂方法来应付。那意味着:订单要一个OrderFactory,发票要一个InvoiceFactory,发货单要一个ShipmentFactory,每个还各自有线上、门店两个实现。业务代码里要造一套线上单据,得这么写:OrderordernewOnlineOrderFactory().create();InvoiceinvoicenewElectronicInvoiceFactory().create();// 得记得选电子ShipmentshipmentnewVirtualShipmentFactory().create();// 得记得选虚拟问题来了:必须搭配这个约束,现在全靠程序员自觉。这三行代码里,你必须自己保证选的都是线上系列——一旦手滑,给线上订单配了个PaperInvoiceFactory(纸质发票),编译器不会报错,代码也能跑,直到线上用户莫名其妙收到一张要邮寄的纸质发票,才发现出了事。而且切换渠道更痛苦。如果某段逻辑要根据渠道整体切换,你得同时改这三行、把三个工厂都从线上系列换成门店系列,一处漏改就出混搭 bug。产品族的核心诉求是成套、一致,而工厂方法各管一列、彼此独立,天然就没法保证跨列的一致性。这就是产品族的困境:我们需要的不是分别造三个产品,而是一次性造出保证配套的一整族产品。谁能提供这种打包保证?抽象工厂。三、抽象工厂登场:一个工厂造一整族抽象工厂的思路很直接:不再为每种产品单独设工厂,而是为每个产品族设一个工厂;这个工厂一口气负责生产该族里的所有产品。先定义各产品的抽象接口(这部分和以前一样):publicinterfaceOrder{voidsubmit();}publicinterfaceInvoice{voidissue();}publicinterfaceShipment{voidship();}关键在工厂接口——它不再只有一个create(),而是每种产品对应一个创建方法,一个工厂声明了造出一整族的能力:// 抽象工厂:声明造这一整族产品的能力publicinterfaceOrderChannelFactory{OrdercreateOrder();InvoicecreateInvoice();ShipmentcreateShipment();}然后,每个渠道(每个产品族)提供一个具体工厂,它造出来的三样东西天然就是配套的:// 线上渠道工厂:造出来的必然是线上全家桶publicclassOnlineChannelFactoryimplementsOrderChannelFactory{publicOrdercreateOrder(){returnnewOnlineOrder();}publicInvoicecreateInvoice(){returnnewElectronicInvoice();}publicShipmentcreateShipment(){returnnewVirtualShipment();}}// 门店渠道工厂:造出来的必然是门店全家桶publicclassStoreChannelFactoryimplementsOrderChannelFactory{publicOrdercreateOrder(){returnnewStoreOrder();}publicInvoicecreateInvoice(){returnnewPaperInvoice();}publicShipmentcreateShipment(){returnnewPickupShipment();}}现在业务代码变成了这样,配套一致性由工厂从结构上保证了:publicclassOrderService{privatefinalOrderChannelFactoryfactory;// 只认一个族工厂publicOrderService(OrderChannelFactoryfactory){this.factoryfactory;}publicvoidplaceOrder(){Orderorderfactory.createOrder();Invoiceinvoicefactory.createInvoice();// 不用操心选哪个牌子Shipmentshipmentfactory.createShipment();// 一定和上面配套// ... 三者天然同属一个渠道,不可能混搭}}// 决定用哪个渠道,只需换一个工厂newOrderService(newOnlineChannelFactory());// 全套线上newOrderService(newStoreChannelFactory());// 全套门店对比第二节的困境,升级点非常清晰:“必须搭配这个约束,从靠程序员自觉”,变成了由具体工厂在代码结构上强制保证。你只要选定了OnlineChannelFactory,它吐出来的三样东西不可能出现门店的版本——混搭在结构上就被杜绝了。而且切换渠道,现在只需要在最外层换一个工厂,业务代码一个字都不用动。这就是抽象工厂的核心价值:保证一族产品的配套一致性,并把整族的切换收敛到一个点。四、角色、骨架与那张网格图抽象工厂的角色和工厂方法类似,但工厂这一侧的职责变重了:角色本例中是谁职责抽象工厂(Abstract Factory)OrderChannelFactory接口声明创建一族产品的一组方法具体工厂(Concrete Factory)OnlineChannelFactory等一个族一个,负责造出本族的全套产品抽象产品(Abstract Product)Order/Invoice/Shipment每种产品的抽象接口具体产品(Concrete Product)OnlineOrder等落在网格某个交叉格里的具体实现理解抽象工厂,最好的方式就是回到第一节那张二维表,把它当成一张网格来看:这张网格图是理解抽象工厂的钥匙,盯住它记三件事:纵向的每一列,是一个产品等级结构(同一种单据的不同实现),由抽象产品接口统领;横向的每一行,是一个产品族(同一渠道的整套单据),由一个具体工厂负责生产一整行;一个具体工厂 网格里的一整行。你选了哪个工厂,就锁定了哪一行,这一行里的产品自动配套。把这张网格刻在脑子里,下一节那个最关键的倾斜性,你一看就懂。五、最关键的取舍:开闭原则的倾斜性这是抽象工厂最重要、也最常被考察的一点,务必吃透。抽象工厂对开闭原则的支持是倾斜的——沿着网格的两个方向扩展,难度天差地别。方向一:增加一个新的产品族(加一行)——非常容易,完美符合开闭。假设现在要新增一个直播带货渠道。你只需要:新建一族具体产品(LiveOrder、LiveInvoice、LiveShipment),再新建一个具体工厂LiveChannelFactory implements OrderChannelFactory,实现那三个创建方法。就完了。抽象工厂接口不用动,已有的线上、门店工厂不用动,业务代码不用动。这个方向,抽象工厂扩展起来行云流水。方向二:增加一个新的产品等级(加一列)——非常痛苦,严重违反开闭。假设现在每笔订单除了订单、发票、发货单,还要多一样优惠券Coupon。你得怎么改?先在抽象工厂接口OrderChannelFactory里,加一个方法Coupon createCoupon();而一旦接口加了方法,所有已经存在的具体工厂——OnlineChannelFactory、StoreChannelFactory、LiveChannelFactory——全部都得跟着实现这个新方法,一个都跑不掉。你被迫回去修改了每一个现存的工厂类,这是彻头彻尾的违反开闭原则。加一列,牵动全身。用一句话总结这个倾斜性:抽象工厂对新增产品族友好(加行容易),对新增产品等级敌视(加列极难)。这不是抽象工厂的 bug,而是它的固有特性——它是为产品族维度频繁变、产品种类维度稳定这种场景量身定做的。这个特性直接决定了它的适用边界:只有当你确信产品的种类(列)基本固定,而产品族(行)会不断增加时,抽象工厂才是绝配。如果你的产品种类本身还在剧烈变动、经常要加新单据,那抽象工厂会让你每加一样东西都痛不欲生,这时候它就是错误的选择。六、什么时候该用,什么时候别碰抽象工厂是三兄弟里最重的,滥用的代价也最大——一上来就是一大堆接口和类。所以它的适用场景很挑,记住这几个前提,同时满足才考虑用:适合用的信号:系统里确实存在成套、配套、不能混搭的产品组合(产品族的概念真实存在),比如换肤(一套皮肤里的按钮、边框、背景必须一致)、跨数据库(一套 MySQL 的连接、语句、结果集,或一套 Oracle 的,不能混用)、跨渠道单据。这些产品族会不断新增,而每族里的产品种类相对固定——正好卡在抽象工厂加行容易的甜区。你希望整族切换能一键完成,且从结构上杜绝混搭。别碰的信号:产品之间根本没有必须配套的关系,只是单纯的多实现——那用工厂方法就够了,别硬套抽象工厂,凭空多出一堆类。产品的种类经常变动(经常要加新的产品等级)——抽象工厂的倾斜性会让你每次都改遍所有工厂,得不偿失。就一个产品族,未来也看不到第二个——那连工厂都未必需要,直接创建即可。还是那句贯穿全系列的话:先确认产品族这个东西在你的业务里真实存在、且会沿着正确的方向(加行)增长,抽象工厂才配得上它的复杂度。为了不存在的配套需求预先搭一套抽象工厂,是这个模式最典型的过度设计——毕竟它一上来就要你写一堆接口,沉没成本很高。七、现实身影,与工厂三兄弟总对比抽象工厂在标准库里有几个非常经典的例子:java.sql.Connection:它就是一个抽象工厂。Connection能createStatement()、prepareStatement()、createBlob()……造出一整族互相配套的数据库操作对象。你连的是 MySQL,拿到的就是 MySQL 那一族实现;连的是 Oracle,就是 Oracle 那一族——族内配套,不会混。这正是跨数据库产品族的教科书案例。javax.xml.parsers.DocumentBuilderFactory、javax.xml.transform.TransformerFactory:名字里带 Factory,通过newInstance()拿到具体工厂,再由它生产配套的解析组件。各类 UI/换肤框架:一套主题工厂负责生产该主题下配套的按钮、文本框、滚动条,保证整个界面风格统一,是抽象工厂最直观的应用。最后,把工厂三兄弟放在一起做个总收束,这也是这三篇的落点:模式解决的问题一句话扩展代价简单工厂创建逻辑散落在业务里一个工厂 switch,收拢创建新增产品要改 switch(违反开闭)工厂方法一种产品的多实现,要频繁扩展一个产品配一个工厂,下放决策新增产品 加一对类(符合开闭)抽象工厂一整族配套产品,要成套创建/切换一个工厂造一整族,保证配套加族容易、加产品种类极难(倾斜)一条清晰的升级链:简单工厂把创建收拢到一处;工厂方法把造哪个下放给子类工厂、解决单一产品的扩展;抽象工厂再升一维,把造哪一族打包给族工厂、解决配套产品的一致性。三者不是三选一,而是随着变化压力从一维走向二维的层层递进。选型时倒过来问自己就行:有没有配套的产品族?有 → 抽象工厂;没有,只是单产品多实现且要频繁扩展?→ 工厂方法;实现稳定、只想收个口?→ 简单工厂。小结。抽象工厂是工厂家族的二维版本,专治一整族产品必须配套使用的问题。它的核心是产品族这个概念——用一个具体工厂负责生产网格里的一整行,从结构上保证族内配套、杜绝混搭,并把整族切换收敛到换一个工厂。而它最需要记住的特质,是开闭原则的倾斜性:新增产品族(加行)轻松无比,新增产品种类(加列)牵一发而动全身。这个倾斜性既是它的适用边界,也是它的选型开关——只有族频繁增、种类稳定的场景才适合它,否则宁可退回工厂方法。到这里,工厂三兄弟就集齐了。下一篇我们离开造哪个/哪族的话题,转向另一个创建难题:当一个对象本身特别复杂、要一步步组装时,该怎么优雅地把它造出来——这就是建造者模式。

相关新闻

2026/8/4 4:08:01

Java单例模式:线程安全实现与最佳实践

1. 单例模式的核心价值与应用场景单例模式可能是设计模式中最简单却又最容易被误用的一个。我在十多年的Java开发经历中,见过太多错误实现单例的案例——有的导致性能问题,有的甚至根本不能保证单例。这个看似简单的模式,实际上蕴含着线程安全…

2026/8/4 4:03:01

Java项目转SpringBoot实战:依赖管理与配置优化

1. 从零开始:普通Java项目转SpringBoot的完整指南去年接手一个遗留的Java Web项目时,我面临着一个典型困境:这个使用传统SSH(StrutsSpringHibernate)架构的项目,配置文件散落在各处,启动需要依赖…

2026/8/4 5:18:06

Keysight E5062A网络分析仪:高频电路测试与S参数测量解析

1. 项目概述:Keysight E5062A网络分析仪的核心价值Keysight E5062A是德科技推出的3GHz射频网络分析仪,作为电子测试测量领域的标杆设备,它专为高频电路和元器件的阻抗特性分析而设计。这款仪器在无线通信、半导体测试和航空航天等领域有着广泛…

2026/8/4 5:18:06

医学大模型深度研究报告(2026年8月版第一部分)

面向医疗AI场景的医学大模型应用能力构建与算法实现路径 深度研究报告 2026年8月 证据分级说明 本报告对所有关键论断标注证据等级,请读者据此判断可信度: 标记 含义 【A】 同行评议论文(Nature/NEJM/JAMA/ACL/NeurIPS 等)或已完成的 RCT 【B】 arXiv 预印本、官方技术报…

2026/8/4 5:18:06

2026 ChinaJoy | 对话飞猫市场总监李乐薇

2026 ChinaJoy在上海盛大启幕,这场数字娱乐行业盛会汇聚众多前沿科技品牌,集中展示创新产品与技术成果。国民随身WiFi品牌飞猫再度重磅登场,依托深厚的技术研发实力、完善的产品布局,持续聚焦用户多元化移动上网需求。在活动现场&…

2026/8/4 5:18:06

ESP32中读取 DHT11 温湿度传感器数据方法

一、DHT11 介绍 DHT11 是一款常用的数字温湿度传感器,适合于需要测量温度和湿度的基本应用场景。其测量范围为湿度为 5%到 95%RH,温度范围为-20℃到 60℃,湿度测量精度为5%RH,温度测量精度为2℃,采用单总线通信协议,通过一个数据引脚完成输入输出双向传输。 DHT11 实物图…

2026/8/3 21:14:30

如何用免费工具突破游戏窗口限制:SRWE完整使用指南

如何用免费工具突破游戏窗口限制:SRWE完整使用指南 【免费下载链接】SRWE Simple Runtime Window Editor 项目地址: https://gitcode.com/gh_mirrors/sr/SRWE 你是否遇到过这样的困扰?想为心爱的游戏截图,却发现游戏不支持自定义分辨率…

2026/8/4 0:02:01

dealsea是什么?跨境卖家必知的美国deal站入门指南

说实话,第一次听说美国这个老牌折扣网站的跨境卖家,十个有八个会问同一个问题:这个平台到底是干嘛的?我见过一个做家居出口的朋友,他在亚马逊上月销二十万美金,却从来没用过它。我给他看了首页——一屏一屏…

2026/8/3 22:40:58

实测才敢推 AI论文网站 2026最新测评与推荐

2026年真正好用的AI论文网站,核心看生成的论文质量、低AI味、格式正确、学术适配四大指标。综合实测,千笔AI、ThouPen、豆包、DeepSeek、Grammarly 是当前最值得推荐的梯队,覆盖从免费到付费、从中文到英文、从文科到理工的全场景需求。一、综…

2026/8/3 13:26:41

2026必备!AI论文网站测评:最新推荐与深度对比

2026年真正好用的AI论文网站,核心看生成的论文质量、低AI味、格式正确、学术适配四大指标。综合实测,千笔AI、ThouPen、豆包、DeepSeek、Grammarly 是当前最值得推荐的梯队,覆盖从免费到付费、从中文到英文、从文科到理工的全场景需求。 一、…

2026/8/3 16:43:13

摆脱论文困扰!盘点2026年全网爆红的的AI论文写作工具

一天写完毕业论文在2026年已不再是天方夜谭。2026年最炸裂、实测能大幅提速的AI论文写作工具,覆盖选题构思、文献整理、内容生成、格式排版等核心场景,真正帮你高效搞定论文难题。 一、全流程王者:一站式搞定论文全链路(一天定稿首…