news 2026/10/1 1:22:12

深入解析 Java 抽象工厂模式:以 java-design-patterns 仓库的王国工厂示例为实战蓝本

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
深入解析 Java 抽象工厂模式:以 java-design-patterns 仓库的王国工厂示例为实战蓝本
  • 示例工程
  • 教程

【免费下载链接】java-design-patterns

Design patterns implemented in Java

项目地址:https://gitcode.com/GitHub_Trending/ja/java-design-patterns
点击查看免费下载

抽象工厂(Abstract Factory)是 GoF 创建型设计模式家族中的核心成员,其本质是"为创建一族相关或相互依赖的对象提供一个接口,而无需指定它们的具体类"。本文以 java-design-patterns 仓库中的abstract-factory模块(阿拉伯语版文档位于 localization/ar/abstract-factory/README.md,英文原版见 abstract-factory/README.md)为主体展开讲解,读者将掌握抽象工厂的完整结构、王国(Kingdom)实战示例的源码级实现、适用场景、取舍权衡,以及如何通过仓库内单元测试验证模式的正确性。

别名与核心意图

该模式在业界也被称为Kit。其核心意图是:

为创建一族相关或相互依赖的对象提供一个接口,而无需指定它们的具体类。

用更通俗的话说:抽象工厂是"工厂的工厂"——它将多个独立但彼此关联/依赖的具体工厂(ElfKingdomFactory、OrcKingdomFactory)聚合在一个统一接口之下,客户端只面向抽象接口编程,完全不感知也不关心最终拿到的是哪个具体实现类。

维基百科对它的定义是:抽象工厂模式提供了一种封装一组具有共同主题的独立工厂的方法,而不指定它们的具体类。

现实世界示例:王国需要成套的对象

要理解抽象工厂,最直观的例子是"创建一座王国"。一个王国需要具有统一主题的一组对象:精灵王国(elven kingdom)需要精灵国王、精灵城堡和精灵军队;兽人王国(orcish kingdom)需要兽人国王、兽人城堡和兽人军队。这些对象之间存在强依赖——不能把精灵国王塞进兽人城堡里,主题必须保持一致。

这正是抽象工厂的用武之地:客户端通过一个工厂接口一次性获取"成套"且主题统一的产品对象,无需关心这些对象各自是如何被实例化的。换个行业类比:一家家具公司用抽象工厂生产多种风格(现代、维多利亚、乡村)的家具,每种风格包含椅子、桌子、沙发,客户端可以一键创建整套现代家具或整套维多利亚家具,风格一致且可整体切换。

程序化示例:从产品接口到工厂实现

翻译上面的王国示例。首先定义王国中的三类产品接口与精灵族的具体实现:

public interface Castle { String getDescription(); } public interface King { String getDescription(); } public interface Army { String getDescription(); } // Elven implementations -> public class ElfCastle implements Castle { static final String DESCRIPTION = "This is the elven castle!"; @Override public String getDescription() { return DESCRIPTION; } } public class ElfKing implements King { static final String DESCRIPTION = "This is the elven king!"; @Override public String getDescription() { return DESCRIPTION; } } public class ElfArmy implements Army { static final String DESCRIPTION = "This is the elven Army!"; @Override public String getDescription() { return DESCRIPTION; } } // Orcish implementations 与此类似,描述文本为 "This is the orc ..."

在仓库中,上述接口分别对应 Castle.java、King.java、Army.java,精灵族实现为 ElfCastle.java、ElfKing.java、ElfArmy.java,兽人族实现为 OrcCastle.java、OrcKing.java、OrcArmy.java。

接下来是王国工厂的抽象与具体实现。KingdomFactory是抽象工厂接口,定义了一组产品创建方法;ElfKingdomFactory与OrcKingdomFactory分别负责组装"精灵套件"和"兽人套件":

public interface KingdomFactory { Castle createCastle(); King createKing(); Army createArmy(); } public class ElfKingdomFactory implements KingdomFactory { @Override public Castle createCastle() { return new ElfCastle(); } @Override public King createKing() { return new ElfKing(); } @Override public Army createArmy() { return new ElfArmy(); } } public class OrcKingdomFactory implements KingdomFactory { @Override public Castle createCastle() { return new OrcCastle(); } @Override public King createKing() { return new OrcKing(); } @Override public Army createArmy() { return new OrcArmy(); } }

对应源码:KingdomFactory.java、ElfKingdomFactory.java、OrcKingdomFactory.java。注意,每个工厂方法的返回类型都是接口类型(Castle、King、Army),具体产品类型被完全隐藏。

FactoryMaker:工厂的工厂

现在我们可以为不同的王国工厂设计一个"工厂选择器"。仓库示例中创建了FactoryMaker,它负责返回ElfKingdomFactory或OrcKingdomFactory的实例。客户端通过FactoryMaker获得所需的具体工厂,再由该工厂产出具体的产品对象(Army、King、Castle的派生类)。示例还使用了enum参数化客户端想要请求的王国工厂类型:

public static class FactoryMaker { public enum KingdomType { ELF, ORC } public static KingdomFactory makeFactory(KingdomType type) { return switch (type) { case ELF -> new ElfKingdomFactory(); case ORC -> new OrcKingdomFactory(); default -> throw new IllegalArgumentException("KingdomType not supported."); }; } }

在仓库中,FactoryMaker以静态内部类的形式定义于 Kingdom.java,其makeFactory方法使用 Java 12+ 的switch表达式语法,按枚举值映射到对应具体工厂。这种写法既保证了可扩展性(新增王国类型只需增加枚举值和 case 分支),也把"选哪个工厂"的决策逻辑集中在一处。

客户端入口:App 主程序

仓库中 App.java 演示了完整的客户端用法。createKingdom方法接收Kingdom.FactoryMaker.KingdomType枚举,通过FactoryMaker.makeFactory取得工厂,再依次调用createKing()、createCastle()、createArmy()把三种产品装配进Kingdom聚合对象:

LOGGER.info("elf kingdom"); createKingdom(Kingdom.FactoryMaker.KingdomType.ELF); LOGGER.info(kingdom.getArmy().getDescription()); LOGGER.info(kingdom.getCastle().getDescription()); LOGGER.info(kingdom.getKing().getDescription()); LOGGER.info("orc kingdom"); createKingdom(Kingdom.FactoryMaker.KingdomType.ORC); LOGGER.info(kingdom.getArmy().getDescription()); LOGGER.info(kingdom.getCastle().getDescription()); LOGGER.info(kingdom.getKing().getDescription());

程序输出(日志格式因运行环境略有差异,字段含义一致):

elf kingdom This is the elven army! This is the elven castle! This is the elven king! orc kingdom This is the orc army! This is the orc castle! This is the orc king!

可以看到:客户端全程只面向KingdomFactory与产品接口编程,切换精灵王国/兽人王国只需改变传入的枚举值,代码零改动。

类图

抽象工厂在该模块中的角色结构与调用关系如下图所示:

抽象工厂类图

从类图(PlantUML 源文件位于 abstract-factory.urm.puml)可以清晰看到三条层次:产品接口Castle/King/Army与各自的具体实现;抽象工厂接口KingdomFactory与两个具体工厂;以及Kingdom.FactoryMaker作为客户端与具体工厂之间的选择器。

适用场景(何时使用抽象工厂)

根据文档,当以下情况出现时应当考虑抽象工厂模式:

  • 系统需要独立于其产品的创建、组合与表示方式;
  • 系统需要被配置为使用多个产品族中的某一个;
  • 一族相关产品对象必须被一起使用,且需要强制这种一致性约束;
  • 希望提供产品类库,只对外暴露接口而不暴露实现;
  • 依赖的生命周期短于使用方(consumer)的生命周期;
  • 依赖需要通过运行时值或参数来构造;
  • 需要在运行时决定使用产品族中的哪一个产品;
  • 需要在运行时已知一个或多个参数之后才能解析依赖;
  • 需要保证产品之间的一致性;
  • 希望新增产品族或新产品时不必修改已有代码。

典型用例包括:

  • 在运行时选择合适的服务实现,例如在FileSystemAcmeService、DatabaseAcmeService、NetworkAcmeService之间切换;
  • 编写单元测试时,用抽象工厂轻松替换为测试替身(stub/mock),大幅降低测试复杂度;
  • 为不同操作系统(OS)的 UI 工具提供各自的产品族实现(类似 Java Swing 的LookAndFeel机制)。

后果与权衡

抽象工厂带来的主要收益:

  • 灵活性(Flexibility):无需修改客户端代码即可在产品族之间轻松切换;
  • 解耦(Decoupling):客户端只与抽象接口交互,提升了可移植性与可维护性;
  • 可复用性(Reusability):抽象工厂与产品便于跨项目复用组件;
  • 可维护性(Maintainability):单个产品族的修改被局部化,更新成本低。

同时也要正视它的代价:

  • 隐藏依赖注入可能引入运行时错误:在 Java 中,把依赖注入隐藏进服务对象内部,可能导致原本可以在编译期捕获的问题推迟到运行时才暴露;
  • 扩展新产品困难:模式擅长创建预先定义好的对象,但要新增一种产品(如给王国加一个"顾问")时,需要同步修改抽象工厂接口与所有具体工厂,改动面较大;
  • 代码复杂度上升:随着大量新接口、新类的引入,代码会比简单直接实现更复杂,需要评估是否值得。

已知的实际应用

抽象工厂在 Java 标准库中有多处经典应用,文档明确列出的包括:

  • javax.xml.parsers.DocumentBuilderFactory——获取解析 XML 文档的DocumentBuilder;
  • javax.xml.transform.TransformerFactory——创建 XSLT 转换器;
  • javax.xml.xpath.XPathFactory——创建 XPath 求值器。

此外,Java Swing 的LookAndFeel类族、Java AWT 中用于创建不同 GUI 组件的各实现,都是抽象工厂思想在生产环境中的真实落地。

与相关模式的关系

  • 工厂方法(Factory Method):抽象工厂通常借助工厂方法来实现产品创建——每个createXxx()本质上就是一个工厂方法;
  • 工厂套件(Factory Kit):与抽象工厂相似,但更侧重于以灵活方式配置和管理一组相关对象;
  • 单例(Singleton):抽象工厂的具体工厂类常被实现为单例,避免重复创建工厂实例带来的开销。

仓库源码与测试验证

本模块的完整实现与测试均可直接查阅:

  • 产品接口与实现:abstract-factory/src/main/java/com/iluwatar/abstractfactory/下的Castle/King/Army及Elf*、Orc*系列类;
  • 工厂抽象与实现:KingdomFactory.java、ElfKingdomFactory.java、OrcKingdomFactory.java;
  • 聚合对象与工厂选择器:Kingdom.java;
  • 程序入口:App.java。

单元测试 AbstractFactoryTest.java 从产品一致性角度验证了模式的正确性:对ELF枚举调用createKingdom后,断言king/castle/army均为Elf*类型且描述文本匹配ElfKing.DESCRIPTION等常量;对ORC同理。这恰好印证了抽象工厂的核心承诺——产品族内部一致性由工厂保证,客户端无需也无法混用不同产品族。

运行方式:该模块是一个标准的 Maven 子模块(见 abstract-factory/pom.xml,主类为com.iluwatar.abstractfactory.App),可在仓库根目录执行./mvnw -pl abstract-factory test运行测试,或使用maven-assembly-plugin打包后直接运行App查看日志输出。

小结

抽象工厂模式通过"接口工厂 + 成套产品"的组合,把对象创建与具体类解耦,让客户端能够以统一方式、零修改地切换整族产品。java-design-patterns 仓库的王国示例结构清晰、可运行可测试,是学习该模式的最佳参考实现;理解KingdomFactory、Elf/OrcKingdomFactory与FactoryMaker三者之间的协作关系,也就掌握了抽象工厂的全部要点。

  • 示例工程
  • 教程

【免费下载链接】java-design-patterns

Design patterns implemented in Java

项目地址:https://gitcode.com/GitHub_Trending/ja/java-design-patterns
点击查看免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/1 1:22:08

FineReport实战:从零搭建企业大数据看板的完整指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/1 1:22:07

Server 2016 安装 OpenSSH Server:在线/离线与公钥配置

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/1 1:22:07

RK3588部署yolov5s:USB摄像头抓帧避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/1 1:21:57

HikariCP连接池泄露定位与排查实战:从告警到防复发

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/1 1:20:43

测试用例设计方法:等价类、边界值、判定表与场景法实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/1 1:20:03

离谱模拟器开发指南:物理交互与性能调优实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华