抽象工厂模式创建产品族工厂方法模式解决的是“一个产品等级结构”的创建问题。比如支付方式只有Payment一种产品接口每个具体工厂创建一个具体支付实现。但现实中有另一类场景系统需要创建一系列相关或相互依赖的对象而且这些对象必须成套使用。比如一个跨平台 UI 库Windows 风格下需要 Windows 按钮和 Windows 复选框Mac 风格下需要 Mac 按钮和 Mac 复选框。你不能在 Windows 风格里混用 Mac 复选框否则界面就乱了。这种“一系列相关对象”就是产品族。抽象工厂模式专门用来创建产品族。定义抽象工厂模式提供一个接口用于创建一系列相关或相互依赖的对象而无需指定它们具体的类。简单说工厂方法生产一个产品抽象工厂生产一套产品。结构抽象工厂模式包含五个角色抽象产品 A、B…定义同一产品族中不同产品的接口。具体产品 A1、B1…实现抽象产品属于同一个产品族。抽象工厂声明创建产品族中所有产品的方法。具体工厂实现抽象工厂创建同一个产品族的具体产品。客户端只依赖抽象工厂和抽象产品不接触具体类。代码示例跨平台 UI假设我们要开发一个跨平台 UI 库支持 Windows 和 Mac 两种风格。每种风格都有按钮和复选框。抽象产品 A按钮publicinterfaceButton{voidrender();}抽象产品 B复选框publicinterfaceCheckbox{voidcheck();}Windows 产品族publicclassWindowsButtonimplementsButton{Overridepublicvoidrender(){System.out.println(渲染 Windows 按钮);}}publicclassWindowsCheckboximplementsCheckbox{Overridepublicvoidcheck(){System.out.println(勾选 Windows 复选框);}}Mac 产品族publicclassMacButtonimplementsButton{Overridepublicvoidrender(){System.out.println(渲染 Mac 按钮);}}publicclassMacCheckboximplementsCheckbox{Overridepublicvoidcheck(){System.out.println(勾选 Mac 复选框);}}抽象工厂publicinterfaceGUIFactory{ButtoncreateButton();CheckboxcreateCheckbox();}具体工厂publicclassWindowsFactoryimplementsGUIFactory{OverridepublicButtoncreateButton(){returnnewWindowsButton();}OverridepublicCheckboxcreateCheckbox(){returnnewWindowsCheckbox();}}publicclassMacFactoryimplementsGUIFactory{OverridepublicButtoncreateButton(){returnnewMacButton();}OverridepublicCheckboxcreateCheckbox(){returnnewMacCheckbox();}}客户端publicclassApplication{privateButtonbutton;privateCheckboxcheckbox;publicApplication(GUIFactoryfactory){buttonfactory.createButton();checkboxfactory.createCheckbox();}publicvoidrender(){button.render();checkbox.check();}publicstaticvoidmain(String[]args){// 切换产品族只需换一个工厂GUIFactoryfactorynewWindowsFactory();ApplicationappnewApplication(factory);app.render();}}客户端只依赖GUIFactory、Button、Checkbox。要换成 Mac 风格只需要把WindowsFactory换成MacFactory其他代码一行不用改。与工厂方法的区别这是最容易混淆的地方。用一句话区分工厂方法一个抽象产品多个具体产品每个具体产品对应一个具体工厂。抽象工厂多个抽象产品多个产品族每个具体工厂负责创建一个产品族。对比维度工厂方法抽象工厂产品等级结构一个多个产品族无此概念有强调成套使用抽象工厂的方法数一个多个扩展方向新增产品容易新增产品族容易新增产品等级困难开闭原则对产品扩展开放对产品族扩展开放对产品等级修改关闭典型场景日志框架、支付方式跨平台 UI、数据库访问层抽象工厂的“开闭原则”是双向的新增一个产品族比如加 Linux 风格只需新增LinuxFactory和相关产品类不用改已有代码。✅新增一个产品等级比如加一个TextField需要修改GUIFactory接口和所有具体工厂。❌所以抽象工厂适合“产品族稳定、产品等级会扩展”的场景。如果产品等级经常变抽象工厂就不合适。另一个经典例子数据库访问层抽象工厂在数据库访问层非常常见。假设系统要支持 MySQL 和 Oracle每种数据库都有连接、命令、事务等对象。// 抽象产品publicinterfaceConnection{voidconnect();}publicinterfaceCommand{voidexecute(Stringsql);}// MySQL 产品族publicclassMySQLConnectionimplementsConnection{...}publicclassMySQLCommandimplementsCommand{...}// Oracle 产品族publicclassOracleConnectionimplementsConnection{...}publicclassOracleCommandimplementsCommand{...}// 抽象工厂publicinterfaceDatabaseFactory{ConnectioncreateConnection();CommandcreateCommand();}// 具体工厂publicclassMySQLFactoryimplementsDatabaseFactory{...}publicclassOracleFactoryimplementsDatabaseFactory{...}客户端通过DatabaseFactory创建连接和命令不关心底层是 MySQL 还是 Oracle。切换数据库时只需换一个工厂实现。优点保证产品族一致性。同一个工厂创建的产品天然属于同一个族不会出现 Windows 按钮配 Mac 复选框的情况。客户端与具体产品解耦。客户端只依赖抽象工厂和抽象产品具体类完全隐藏。符合开闭原则产品族维度。新增产品族时不需要修改已有代码。集中管理产品族。产品族的切换只需换一个具体工厂。缺点扩展产品等级困难。新增一个产品接口要修改抽象工厂和所有具体工厂违反开闭原则。抽象工厂接口庞大。产品族中产品越多抽象工厂的方法就越多接口会变得臃肿。类数量增加。每个产品族都要一套具体产品类和一个具体工厂类的数量增长很快。适用场景系统需要创建多个相关对象且它们必须成套使用。系统需要支持多种“风格”或“平台”且风格之间相互独立。客户端不关心具体产品类只依赖抽象接口。产品族相对稳定产品等级可能扩展。典型应用跨平台 UI 库Windows、Mac、Linux 风格。数据库访问层MySQL、Oracle、PostgreSQL。游戏中的不同种族人族、兽族、神族各有自己的建筑和兵种。不同主题的报表生成器。在 JDK 和 Spring 中的体现JDK 中的抽象工厂javax.xml.parsers.DocumentBuilderFactory是一个抽象工厂。它提供newDocumentBuilder()创建DocumentBuilder还提供newDocument()等创建相关对象。具体工厂由系统属性或服务提供者决定客户端不直接new。java.sql.Connection可以看作抽象工厂它提供createStatement()、prepareStatement()等方法返回同一数据库产品族中的命令对象。Spring 中的体现Spring 的BeanFactory体系可以看作抽象工厂。BeanFactory是抽象工厂XmlBeanFactory、AnnotationConfigApplicationContext是具体工厂它们负责创建整个应用所需的各种 Bean。客户端通过getBean()获取对象不关心创建细节。不过 Spring 的 BeanFactory 比经典抽象工厂更灵活它不限定产品族而是根据配置动态创建任意类型的 Bean。总结抽象工厂模式的核心是产品族。它把一组相关对象的创建集中到一个工厂接口中保证客户端拿到的是同一族的产品。工厂方法关注“一个产品的创建”抽象工厂关注“一族产品的创建”。选择哪种工厂模式取决于你的产品结构只有一个产品等级且需要扩展产品 → 工厂方法。有多个产品等级需要成套创建 → 抽象工厂。产品很少且稳定 → 简单工厂。理解产品族和产品等级这两个概念就能准确判断什么时候该用抽象工厂。
