设计模式 04 · 抽象工厂模式

发布时间:2026/8/4 4:08:08

设计模式 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(违反开闭)工厂方法一种产品的多实现,要频繁扩展一个产品配一个工厂,下放决策新增产品 加一对类(符合开闭)抽象工厂一整族配套产品,要成套创建/切换一个工厂造一整族,保证配套加族容易、加产品种类极难(倾斜)一条清晰的升级链:简单工厂把创建收拢到一处;工厂方法把造哪个下放给子类工厂、解决单一产品的扩展;抽象工厂再升一维,把造哪一族打包给族工厂、解决配套产品的一致性。三者不是三选一,而是随着变化压力从一维走向二维的层层递进。选型时倒过来问自己就行:有没有配套的产品族?有 → 抽象工厂;没有,只是单产品多实现且要频繁扩展?→ 工厂方法;实现稳定、只想收个口?→ 简单工厂。小结。抽象工厂是工厂家族的二维版本,专治一整族产品必须配套使用的问题。它的核心是产品族这个概念——用一个具体工厂负责生产网格里的一整行,从结构上保证族内配套、杜绝混搭,并把整族切换收敛到换一个工厂。而它最需要记住的特质,是开闭原则的倾斜性:新增产品族(加行)轻松无比,新增产品种类(加列)牵一发而动全身。这个倾斜性既是它的适用边界,也是它的选型开关——只有族频繁增、种类稳定的场景才适合它,否则宁可退回工厂方法。到这里,工厂三兄弟就集齐了。下一篇我们离开造哪个/哪族的话题,转向另一个创建难题:当一个对象本身特别复杂、要一步步组装时,该怎么优雅地把它造出来——这就是建造者模式。

相关新闻

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

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

2026/8/4 4:08:08

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

ChatGPT Plus已经够强了,为什么使用Codex后还是频繁遇到额度瓶颈?

ChatGPT Plus已经够强了,为什么使用Codex后还是频繁遇到额度瓶颈?

2026/8/4 4:08:08

很多用户升级ChatGPT Plus以后,日常对话、写作、文件分析和代码问答都已经足够流畅。但真正开始使用Codex处理项目后,却会产生一种明显落差:明明没有写多少代码,为什么额度消耗得这么快? 明明只是修改一个功能&#xf…

Nginx配置优化:深入理解ngx_http_merge_locations指令

Nginx配置优化:深入理解ngx_http_merge_locations指令

2026/8/4 4:08:08

1. 理解ngx_http_merge_locations的核心作用ngx_http_merge_locations是Nginx配置优化中一个经常被忽视但极其重要的指令。它负责处理location块之间的继承与合并逻辑,直接影响着请求路由的优先级和配置继承关系。当你在Nginx配置中定义了多个嵌套或同级的location块…

实战电话号码定位解决方案:如何高效实现地理位置精准查询

实战电话号码定位解决方案:如何高效实现地理位置精准查询

2026/8/4 5:08:11

实战电话号码定位解决方案:如何高效实现地理位置精准查询 【免费下载链接】location-to-phone-number This a project to search a location of a specified phone number, and locate the map to the phone number location. 项目地址: https://gitcode.com/gh_m…

BepInEx 6.0跨平台架构解析:从插件加载到Harmony集成的Unity模组开发指南

BepInEx 6.0跨平台架构解析:从插件加载到Harmony集成的Unity模组开发指南

2026/8/4 5:08:11

1. 项目概述:为什么我们需要深入理解BepInEx 6.0如果你是一名Unity游戏模组开发者,或者你正在为某个Unity游戏开发插件,那么“BepInEx”这个名字对你来说一定不陌生。它几乎是当前Unity游戏社区插件生态的基石,从《英灵神殿》到《…

Java/PHP/Python运行时Hook技术与反Hook攻防实战

Java/PHP/Python运行时Hook技术与反Hook攻防实战

2026/8/4 5:08:11

1. 运行时Hook技术基础回顾在上一篇文章中,我们已经详细介绍了Java、PHP和Python三种语言的运行时Hook基本原理和常见实现方式。简单回顾一下,运行时Hook技术主要通过在程序执行过程中动态修改函数调用、方法实现或系统API调用,实现对程序行为…

打破壁垒,联防联控:构建军警民一体化的要地安保“共治底座”

打破壁垒,联防联控:构建军警民一体化的要地安保“共治底座”

2026/8/4 5:08:11

打破壁垒,联防联控:构建军警民一体化的要地安保“共治底座”一、建设背景与传统多方防控体系痛点党政核心枢纽、国防战备要地、城市重点商圈、重大活动场馆、交通能源核心设施等关键防护目标,是国家安全、社会稳定、民生运行的核心战略载体&a…

告别入门迷茫!4步拆解LLM Agent核心工程,速通大模型实战

告别入门迷茫!4步拆解LLM Agent核心工程,速通大模型实战

2026/8/4 5:08:11

想学LLM Agent的人,八成卡在第一关:网上的教程要么停在"写提示词",要么一上来甩你一堆框架名,看完更懵。 这其实不怪你。Agent工程的坑在于——它是一个层层叠加的东西,每个名词管的事都不一样。把层次拆开&…

3步彻底解决Mac NTFS读写难题:免费开源Nigate工具终极指南

3步彻底解决Mac NTFS读写难题:免费开源Nigate工具终极指南

2026/8/4 4:58:10

3步彻底解决Mac NTFS读写难题:免费开源Nigate工具终极指南 【免费下载链接】Free-NTFS-for-Mac Nigate: An open-source NTFS utility for Mac. It supports all Mac models (Intel and Apple Silicon), providing full read-write access, mounting, and managemen…

ncmdumpGUI:一键解锁网易云音乐ncm文件的终极解决方案

ncmdumpGUI:一键解锁网易云音乐ncm文件的终极解决方案

2026/8/3 4:49:52

ncmdumpGUI:一键解锁网易云音乐ncm文件的终极解决方案 【免费下载链接】ncmdumpGUI C#版本网易云音乐ncm文件格式转换,Windows图形界面版本 项目地址: https://gitcode.com/gh_mirrors/nc/ncmdumpGUI 你是否曾经从网易云音乐下载了心爱的歌曲&am…

分布式配置中心选型实战:Nacos与Consul在创业场景下的对比

分布式配置中心选型实战:Nacos与Consul在创业场景下的对比

2026/8/3 19:24:18

分布式配置中心选型实战:Nacos与Consul在创业场景下的对比工程导读:本文深入讨论 分布式配置中心选型实战:Nacos与Consul在创业场景下的对比 在生产工程实践中的核心落地方案。基于 分布式架构与微服务设计 视角,剖析实际痛点、架…

MoneyPrinterPlus实战指南:AI视频批量生成与自动化发布完整解决方案

MoneyPrinterPlus实战指南:AI视频批量生成与自动化发布完整解决方案

2026/8/3 20:38:37

MoneyPrinterPlus实战指南:AI视频批量生成与自动化发布完整解决方案 【免费下载链接】MoneyPrinterPlus AI一键批量生成各类短视频,自动批量混剪短视频,自动把视频发布到抖音,快手,小红书,视频号上,赚钱从来没有这么容易过! 支持本地语音模型chatTTS,fasterwhisper,…

3步解决Windows DLL缺失问题:VisualCppRedist AIO终极运行库修复方案

3步解决Windows DLL缺失问题:VisualCppRedist AIO终极运行库修复方案

2026/8/4 0:07:58

3步解决Windows DLL缺失问题:VisualCppRedist AIO终极运行库修复方案 【免费下载链接】vcredist AIO Repack for latest Microsoft Visual C Redistributable Runtimes 项目地址: https://gitcode.com/gh_mirrors/vc/vcredist 你是否曾经在打开游戏或软件时遇…

SingleFile终极指南:一键保存完整网页的5大核心功能

SingleFile终极指南:一键保存完整网页的5大核心功能

2026/8/4 0:07:58

SingleFile终极指南:一键保存完整网页的5大核心功能 【免费下载链接】SingleFile Web Extension for saving a faithful copy of a complete web page in a single HTML file 项目地址: https://gitcode.com/gh_mirrors/si/SingleFile 你是否曾经遇到过这样的…

国家中小学智慧教育平台电子课本下载终极方案:三步免费获取PDF教材

国家中小学智慧教育平台电子课本下载终极方案:三步免费获取PDF教材

2026/8/4 0:07:58

国家中小学智慧教育平台电子课本下载终极方案:三步免费获取PDF教材 【免费下载链接】tchMaterial-parser 国家中小学智慧教育平台 电子课本下载工具,帮助您从智慧教育平台中获取电子课本的 PDF 文件网址并进行下载,让您更方便地获取课本内容。…

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

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

2026/8/2 17:06:42

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

导师推荐!2026最新AI论文工具测评与实用推荐

导师推荐!2026最新AI论文工具测评与实用推荐

2026/8/3 7:25:44

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

告别游戏崩溃:XCOM 2模组管理器的智能革命

告别游戏崩溃:XCOM 2模组管理器的智能革命

2026/8/3 2:41:27

告别游戏崩溃:XCOM 2模组管理器的智能革命 【免费下载链接】xcom2-launcher The Alternative Mod Launcher (AML) is a replacement for the default game launchers from XCOM 2 and XCOM Chimera Squad. 项目地址: https://gitcode.com/gh_mirrors/xc/xcom2-lau…