文章中如果有图看不到,可以点这里去 csdn 看看。从那边导过来的,文章太多,没法一篇篇修改好。

深入浅出设计模式【三、抽象工厂模式】

一、抽象工厂模式介绍

抽象工厂模式,又称工具箱(Kit),是一种创建型设计模式。它能创建一系列相关或相互依赖的对象,而无需指定它们具体的类

该模式提供了一个高层次的接口,用于创建整个产品族(a family of products),而不是单个产品。客户端代码通过这个抽象接口与工厂交互,从而与具体产品的实现解耦。这使得在不修改客户端代码的情况下,切换整个产品族变得非常容易(例如,从一套“现代风格”的UI组件切换到一套“古典风格”的UI组件)。

二、核心概念与意图

  1. 核心概念

    • 抽象工厂 (Abstract Factory): 声明一个创建一系列抽象产品(属于同一产品族)的操作接口。
    • 具体工厂 (Concrete Factory): 实现抽象工厂的接口,负责创建属于特定产品变体族(如“现代”或“古典”)的具体产品对象。
    • 抽象产品 (Abstract Product): 为产品族中的每种产品类型声明一个接口(如 Button, TextField)。
    • 具体产品 (Concrete Product): 实现抽象产品接口,由具体工厂创建。这些产品组合在一起构成了一个产品族(如 ModernButton, ModernTextField)。
    • 客户端 (Client): 只使用由抽象工厂和抽象产品声明的接口。它不知道正在使用的是哪个具体工厂或具体产品。
  2. 意图

    • 提供一个接口,用于创建相关的或依赖对象的家族,而不需要明确指定具体类
    • 确保一系列相关的产品被一起使用,保持产品之间的一致性。
    • 将客户端与具体的产品类解耦,支持产品族和产品类型的轻松切换。

三、适用场景剖析

抽象工厂模式在以下场景中极为有效:

  1. 系统需要独立于其产品的创建、组合和表示时: 当希望配置系统使用多种产品系列(变体)中的一种时。
  2. 系统需要由多个相关产品对象来构成一个产品族,并希望保证它们一起使用时: 例如,一个GUI应用需要确保所有UI组件(按钮、文本框、下拉框)都具有同一种视觉风格(如“macOS风格”或“Windows风格”),不能混用。
  3. 需要提供一个产品类库,但只想暴露它们的接口,而不是实现时: 隐藏具体的产品类和创建过程。

关键区别

  • 工厂方法模式: 关注于创建单一产品,并通过子类来决定实例化哪个具体产品。
  • 抽象工厂模式: 关注于创建整个产品族(多个相关产品),通常通过具体工厂类(而不是子类化)来实现。一个具体工厂通常使用多个工厂方法来创建不同类型的产品。

四、类图解析

以下Mermaid类图清晰地展示了抽象工厂模式的结构和角色间的关系:

creates
creates
creates
creates
collaborates with
«interface»
AbstractFactory
+createProductA() : AbstractProductA
+createProductB() : AbstractProductB
ConcreteFactory1
+createProductA() : AbstractProductA
+createProductB() : AbstractProductB
ConcreteFactory2
+createProductA() : AbstractProductA
+createProductB() : AbstractProductB
«interface»
AbstractProductA
+usefulFunctionA()
«interface»
AbstractProductB
+usefulFunctionB()
+anotherUsefulFunctionB(AbstractProductA collaborator)
ProductA1
+usefulFunctionA()
ProductA2
+usefulFunctionA()
ProductB1
+usefulFunctionB()
+anotherUsefulFunctionB(AbstractProductA collaborator)
ProductB2
+usefulFunctionB()
+anotherUsefulFunctionB(AbstractProductA collaborator)
  • AbstractFactory: 声明一组创建抽象产品的方法 (createProductA, createProductB)。
  • ConcreteFactory1, ConcreteFactory2: 实现 AbstractFactory 接口,创建属于特定产品族(如风格1、风格2)的具体产品对象。
  • AbstractProductA, AbstractProductB: 为每种产品类型声明接口。
  • ProductA1, ProductA2, ProductB1, ProductB2: 实现抽象产品接口的具体类。ProductA1ProductB1 属于同一产品族(由 ConcreteFactory1 创建),ProductA2ProductB2 属于另一产品族(由 ConcreteFactory2 创建)。
  • 客户端: 依赖于 AbstractFactoryAbstractProduct 接口。它通过 AbstractFactory 获取产品,并通过 AbstractProduct 接口使用产品。

五、各种实现方式及其优缺点

1. 经典实现(基于接口/抽象类)

即上述UML所描述的标准方式,为每个产品族定义一个具体的工厂类。

  • 优点
    • 极强的产品族一致性保证: 一个具体工厂只创建属于同一族的产品,绝不会出现风格混搭。
    • 符合开闭原则: 要引入新的产品族(如“未来风格”),只需添加新的具体工厂类和具体产品类,无需修改现有代码。
    • 符合单一职责原则: 将产品创建逻辑集中到工厂类中。
    • 符合依赖倒置原则: 客户端代码只依赖于抽象。
  • 缺点
    • 难以支持“新种类产品”: 这是最大的缺点。如果需要在产品族中添加一种全新的产品类型(例如,在UI组件族中加入一个新的 Slider),就需要修改 AbstractFactory 接口及其所有具体实现类,这违反了开闭原则。

2. 使用反射或配置化

通过配置文件(如XML、Properties)或依赖注入容器来指定要创建的具体工厂类,从而避免在代码中硬编码 new ConcreteFactory1()

// 简单示例:通过属性文件获取工厂类名
public class FactoryProvider {
    private static final String FACTORY_TYPE = getFactoryTypeFromConfig(); // 从配置读取

    public static AbstractFactory getFactory() {
        switch (FACTORY_TYPE) {
            case "Modern":
                return new ModernFactory();
            case "Classic":
                return new ClassicFactory();
            default:
                throw new IllegalArgumentException("Unknown factory type");
        }
        // 或者使用反射: return (AbstractFactory) Class.forName(className).newInstance();
    }
}
// 客户端:AbstractFactory factory = FactoryProvider.getFactory();
  • 优点
    • 更高的灵活性: 可以在不重新编译代码的情况下切换整个产品族。
  • 缺点
    • 增加了配置的复杂性。
    • 类型安全问题依然存在。

六、最佳实践

  1. 将工厂实现为单例: 一个具体工厂实例通常是无状态的,并且在整个应用中只需要一个实例。因此,通常将其实现为单例。
  2. 优先使用依赖注入(DI)不要手动实现抽象工厂模式! 在现代应用中,应直接使用 Spring等IoC容器 来充当这个“超级抽象工厂”。你通过配置(XML或注解)来定义哪个具体产品(Bean)属于哪个“族”(Profile/Configuration),容器会自动完成装配。这是抽象工厂模式思想的最佳实践。
  3. 明确边界: 只有在确实存在“产品族”概念,且需要保证族内产品的一致性时,才使用抽象工厂模式。如果产品之间没有这种强关联关系,使用多个工厂方法可能更合适。
  4. 应对“新产品”扩展问题: 如果预见到产品类型可能会频繁增加,可以考虑对工厂接口使用“参数化创建”方法(如 createProduct(String type)),但这会牺牲类型安全性和清晰性,需谨慎权衡。

七、在开发中的演变和应用

抽象工厂模式的思想是现代化框架和开发的基石:

  1. IoC容器(Spring Framework): Spring的 ApplicationContext 是终极的抽象工厂实现。它管理着大量的Bean(产品),并且可以根据不同的配置(如不同的 @Profile)来提供完全不同的一套Bean实现(整个产品族)。例如,为“dev”环境提供一组基于内存数据库的DAO实现,为“prod”环境提供一组基于MySQL数据库的DAO实现。
  2. 跨平台开发与适配: 该模式非常适合屏蔽不同平台或技术的差异。例如,一个图形应用使用抽象工厂接口,其具体实现可以是 WindowsFactory, MacFactory, LinuxFactory,它们分别创建各自平台原生风格的UI组件。客户端代码无需改变即可跨平台运行。
  3. 微服务配置: 在微服务架构中,为不同部署环境(如中国区、欧美区)提供一套完全不同的中间件客户端配置(如Redis、MQ),也可以视为抽象工厂模式的应用。

八、真实开发案例(Java语言内部、知名开源框架、工具)

  1. Java XML处理 (JAXP)

    • javax.xml.parsers.DocumentBuilderFactory 是一个抽象工厂。
    • newInstance() 方法返回一个具体工厂实例(如 com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl)。
    • 这个具体工厂可以创建一系列相关的产品:DocumentBuilder, SchemaFactory 等,它们全部来自Xerces这个产品族。
  2. Java数据库连接 (JDBC)

    • java.sql.Connection 接口可以看作是一个抽象工厂。
    • 当你通过 DriverManager.getConnection(url) 获取一个具体的Connection对象(如MySQL的 ConnectionImpl)时,这个具体连接就是一个具体工厂。
    • 它可以创建一系列与MySQL相关的Statement产品族对象:Statement, PreparedStatement, CallableStatement
    • 切换数据库驱动(URL)就意味着切换了整个SQL语句产品的实现族。
  3. Spring Framework

    • @Profile 注解: 这是抽象工厂模式最典型的应用。
    @Configuration
    public class AppConfig {
    
        @Bean
        @Profile("dev") // 开发环境产品族
        public DataSource devDataSource() {
            return new EmbeddedDatabaseBuilder().build();
        }
    
        @Bean
        @Profile("prod") // 生产环境产品族
        public DataSource prodDataSource() {
            return new MySqlDataSource(); // 假设的MySQL实现
        }
    }
    

    通过激活不同的Profile,Spring容器会提供完全不同的一套数据源、仓库实现等,完美体现了抽象工厂的精神。

  4. 日志门面 (SLF4J)

    • SLF4J作为门面,本身不实现日志功能。它绑定不同的日志实现(如Logback, Log4j 2)。
    • 每个绑定包(如 slf4j-log4j12.jar)就相当于一个具体工厂,它提供了一整套与Log4j 1.2相关的日志产品(Logger, Appender等)。切换绑定包就切换了整个日志实现族。

九、总结

方面总结
模式类型创建型设计模式
核心意图创建相关或依赖对象的家族,而无需指定具体类,保证产品间的一致性。
关键角色抽象工厂 (AbstractFactory)、具体工厂 (ConcreteFactory)、抽象产品 (AbstractProduct)、具体产品 (ConcreteProduct)
主要优点1. 强一致性:确保产品族内的产品兼容。
2. 解耦客户端与具体类:客户端只面向接口编程。
3. 易于切换产品族:通过更换具体工厂即可。
4. 符合开闭原则(对产品族):支持新增产品族。
主要缺点1. 难以支持“新产品类型”:增加新产品需修改所有工厂接口和类,违反开闭原则。
2. 代码复杂度高:需要大量的类和接口。
适用场景1. 系统需要配置多个产品族中的一种。
2. 需要保证一系列相关产品一起使用。
3. 需要提供一个产品库,并只想暴露接口。
关系与对比vs. 工厂方法: 抽象工厂通常通过多个工厂方法实现;工厂方法创建一种产品,抽象工厂创建产品族。
vs. 建造者模式: 建造者关注如何分步创建一个复杂对象,而抽象工厂关注创建多个不同对象。
现代应用Spring IoC容器是其思想的最佳实践,通过配置管理整个Bean“产品族”。

抽象工厂模式是架构师设计大型、可配置、可扩展系统的重要工具。它通过定义清晰的抽象接口和产品族概念,极大地提升了系统的模块化水平和可维护性。在现代开发中,其思想已融入各种IoC容器和框架中,成为软件开发的基石之一。

posted @ 2025-08-29 12:59  NeoLshu  阅读(7)  评论(0)    收藏  举报  来源