设计模式——抽象工厂设计模式(创建型)

摘要

抽象工厂设计模式是一种创建型设计模式,旨在提供一个接口,用于创建一系列相关或依赖的对象,无需指定具体类。它通过抽象工厂、具体工厂、抽象产品和具体产品等组件构建,相比工厂方法模式,能创建一个产品族。该模式适用于多个产品需一起创建的场景,可隐藏产品细节,便于客户端使用。

1. 抽象工厂设计模式定义

抽象工厂模式创建型设计模式 的一种,它提供一个接口,用于创建一系列相关或相互依赖的对象 ,而无需指定它们的具体类。就像一个"超级工厂",里面包含多个子工厂,用于生产同一产品族中的各种产品。你不关心产品的具体实现,只关心工厂能提供什么样的系列产品。

核心要点:

|---------|------------------------------|
| 维度 | 描述 |
| 意图 | 为创建相关对象的家族提供一个统一的接口 |
| 解决问题 | 解决"多个产品之间需要一起创建"的问题 |
| 与工厂方法对比 | 工厂方法是一个产品一个工厂;抽象工厂是一个产品族一个工厂 |
| 隐藏细节 | 客户端不需要知道具体产品类,只通过抽象接口使用 |

2. 抽象工厂设计模式结构

2.1. 抽象工厂设计模式类图

  • AbstractFactory:抽象工厂
  • ConcreteFactory:具体工厂
  • AbstractProduct:抽象产品
  • Product:具体产品

2.2. 抽象工厂设计模式时序图

3. 抽象工厂设计模式实现方式

抽象工厂模式的实现,主要通过一组产品接口 + 一个抽象工厂接口 + 多个具体工厂实现类来构建。下面是完整的标准实现方式。

3.1. 🧱 实现步骤(Java 示例)

3.1.1. 定义抽象产品接口

复制代码
// 抽象产品 A
public interface Button {
    void click();
}

// 抽象产品 B
public interface TextField {
    void input(String text);
}

3.1.2. 定义具体产品类

复制代码
// Windows 系列产品
public class WindowsButton implements Button {
    public void click() {
        System.out.println("Windows 按钮点击");
    }
}

public class WindowsTextField implements TextField {
    public void input(String text) {
        System.out.println("Windows 输入: " + text);
    }
}

// Mac 系列产品
public class MacButton implements Button {
    public void click() {
        System.out.println("Mac 按钮点击");
    }
}

public class MacTextField implements TextField {
    public void input(String text) {
        System.out.println("Mac 输入: " + text);
    }
}

3.1.3. 定义抽象工厂接口

复制代码
public interface GUIFactory {
    Button createButton();
    TextField createTextField();
}

3.1.4. 实现具体工厂类

复制代码
public class WindowsFactory implements GUIFactory {
    public Button createButton() {
        return new WindowsButton();
    }

    public TextField createTextField() {
        return new WindowsTextField();
    }
}

public class MacFactory implements GUIFactory {
    public Button createButton() {
        return new MacButton();
    }

    public TextField createTextField() {
        return new MacTextField();
    }
}

3.1.5. 客户端使用示例

复制代码
public class Application {
    
    public static void main(String[] args) {
        
        // 可通过配置文件或环境变量来控制
        GUIFactory factory = new WindowsFactory();
        // GUIFactory factory = new MacFactory();

        Button button = factory.createButton();
        TextField textField = factory.createTextField();

        button.click();
        textField.input("Hello World!");
    }
}

3.2. ✅ 抽象工厂模式特点

|------|---------------------------|
| 特点 | 描述 |
| 解耦 | 客户端不需要了解产品的具体类 |
| 易于扩展 | 可以轻松新增新的产品族,只需添加一个新的工厂类 |
| 成本 | 新增产品种类时,需要修改所有工厂类(违背开闭原则) |
| 应用场景 | 多产品族、产品组合固定、创建逻辑复杂的场景 |

4. 抽象工厂设计模式适合场景

抽象工厂是一种创建型设计模式 ,适用于需要一组相关或互相依赖的对象的场景。

4.1. ✅ 适合使用抽象工厂模式的场景

|---------------------------------|-------------------------------------------|
| 场景 | 说明 |
| 产品族固定,且产品之间有依赖关系 | 比如 GUI 界面中,按钮、文本框等控件需风格统一(如 Windows、Mac)。 |
| 系统需要独立于产品创建逻辑 | 客户端无需关心产品如何创建,只关注使用接口。 |
| 需要保证产品之间的一致性(风格/协议/行为) | 比如同一品牌的组件应配套使用,避免混搭出错。 |
| 系统有多个产品族,但每次只使用其中一个 | 比如数据库连接池的不同厂商实现(Druid、HikariCP)。 |
| 适合用于"横向扩展"产品族,而不是"纵向扩展"产品种类 | 可以增加新的平台或风格,但不易增加产品接口。 |

4.2. ❌ 不适合使用抽象工厂模式的场景

|--------------------------|------------------------------------|
| 场景 | 原因 |
| ❌ 只需要创建一种对象,不是一个产品族 | 比如只创建不同类型的日志对象,用简单工厂或策略更合适。 |
| ❌ 频繁增加新的产品(产品等级结构) | 每新增一个产品类型(接口),所有具体工厂都要修改,违背"开闭原则"。 |
| ❌ 产品之间无依赖、不要求一致性 | 没有必要引入抽象工厂,使用简单工厂或直接实例化即可。 |
| ❌ 产品构造非常简单,扩展性需求低 | 抽象工厂的结构较复杂,维护成本较高,可能得不偿失。 |
| ❌ 仅用于单一系统或开发周期很短的小项目 | 引入抽象工厂会增加架构复杂度。 |

4.3. 📌 抽象工厂设计模式总结

|-------------|--------|-----|
| 项目 | 抽象工厂适用 | 不适用 |
| 是否一组产品族 | ✅ 是 | ❌ 否 |
| 是否要求统一风格 | ✅ 是 | ❌ 否 |
| 是否频繁新增产品接口 | ❌ 否 | ✅ 是 |
| 是否希望屏蔽实例化逻辑 | ✅ 是 | ❌ 否 |

5. 抽象工厂设计模式实战示例

在金融风控系统中,不同的业务线(如消费贷、车贷、现金贷等)往往使用不同的风控规则引擎或评分模型。抽象工厂模式在这里可以用于隔离不同业务线的策略实现,提升系统的可扩展性和解耦能力。

5.1. ✅ 多业务线风控规则工厂示例:

系统支持多个业务线,每个业务线都有自己的一套风控规则引擎(如用户画像评分、反欺诈规则、额度评估等),需要统一接口、按业务线隔离实现。

5.2. 🧱 抽象设计结构

  • 抽象产品:RiskRuleEngine(风控规则引擎)
  • 抽象工厂:RiskRuleFactory(风控规则工厂)
  • 具体工厂:ConsumerLoanFactoryCarLoanFactory
  • 客户端:风控服务,根据业务类型获取对应工厂并执行规则引擎

5.3. 🧩 代码结构

5.3.1. 抽象产品接口

复制代码
public interface RiskRuleEngine {
    void evaluate(String userId);
}

5.3.2. 抽象工厂接口

复制代码
public interface RiskRuleFactory {
    RiskRuleEngine createUserProfileEngine();
    RiskRuleEngine createFraudEngine();
    RiskRuleEngine createCreditLimitEngine();
}

5.3.3. 具体产品实现(以消费贷为例)

复制代码
@Component
public class ConsumerUserProfileEngine implements RiskRuleEngine {
    public void evaluate(String userId) {
        System.out.println("消费贷 - 用户画像评估:" + userId);
    }
}

@Component
public class ConsumerFraudEngine implements RiskRuleEngine {
    public void evaluate(String userId) {
        System.out.println("消费贷 - 反欺诈规则评估:" + userId);
    }
}

@Component
public class ConsumerCreditLimitEngine implements RiskRuleEngine {
    public void evaluate(String userId) {
        System.out.println("消费贷 - 授信额度评估:" + userId);
    }
}

5.3.4. 消费贷工厂实现

复制代码
@Component("consumerLoanFactory")
public class ConsumerLoanFactory implements RiskRuleFactory {

    @Autowired private ConsumerUserProfileEngine userProfileEngine;
    @Autowired private ConsumerFraudEngine fraudEngine;
    @Autowired private ConsumerCreditLimitEngine creditLimitEngine;

    public RiskRuleEngine createUserProfileEngine() {
        return userProfileEngine;
    }

    public RiskRuleEngine createFraudEngine() {
        return fraudEngine;
    }

    public RiskRuleEngine createCreditLimitEngine() {
        return creditLimitEngine;
    }
}

5.3.5. 客户端服务按业务线执行风控评估

复制代码
@Service
public class RiskControlService {

    @Autowired
    @Qualifier("consumerLoanFactory") // 或通过配置中心动态选择工厂
    private RiskRuleFactory riskRuleFactory;

    public void doRiskEvaluate(String userId) {
        riskRuleFactory.createUserProfileEngine().evaluate(userId);
        riskRuleFactory.createFraudEngine().evaluate(userId);
        riskRuleFactory.createCreditLimitEngine().evaluate(userId);
    }
}

5.4. ✅ 如果要支持动态业务线切换(如配置中心配置当前业务)

你可以将所有工厂注册进一个 Map:

复制代码
@Component
public class RiskRuleFactoryRegistry {

    private final Map<String, RiskRuleFactory> factoryMap = new HashMap<>();

    @Autowired
    public RiskRuleFactoryRegistry(List<RiskRuleFactory> factories) {
        for (RiskRuleFactory factory : factories) {
            factoryMap.put(factory.getClass().getSimpleName().replace("Factory", "").toLowerCase(), factory);
        }
    }

    public RiskRuleFactory getFactory(String bizType) {
        return factoryMap.get(bizType.toLowerCase());
    }
}

5.5. ✅ 使用配置中心动态切换工厂

复制代码
@Service
public class DynamicRiskControlService {

    @Value("${biz.line.type:consumerLoan}")
    private String bizType;

    @Autowired
    private RiskRuleFactoryRegistry registry;

    public void doEvaluate(String userId) {
        RiskRuleFactory factory = registry.getFactory(bizType);
        factory.createUserProfileEngine().evaluate(userId);
        factory.createFraudEngine().evaluate(userId);
        factory.createCreditLimitEngine().evaluate(userId);
    }
}

5.6. ✅ 抽象工厂涉及面模式总结

|-------|------------------------|
| 优势 | 描述 |
| 模块隔离 | 不同业务线的规则逻辑完全隔离,避免混乱 |
| 易于扩展 | 新业务线只需实现一套工厂和引擎,不影响老业务 |
| 支持插件化 | 工厂可结合 SPI 动态加载 |
| 配置驱动 | 支持配置中心动态切换业务逻辑 |

6. 抽象工厂设计模式思考

6.1. 引入SPI 加载抽象工厂、或者结合 Spring Boot Starter 自动注册不同业务线的风控策略示例

6.1.1. 需求回顾

  • 多业务线(消费贷、车贷等)各自实现一套 RiskRuleFactory
  • 通过 SPI 机制实现工厂插件化,支持 jar 扩展、热插拔。
  • Spring Boot 启动时自动扫描 SPI 实现,注册到工厂注册中心。
  • 支持根据配置动态切换当前业务线风控工厂。

6.1.2. 项目结构示例

复制代码
risk-control-spi/
 ├─ META-INF/services/com.example.risk.spi.RiskRuleFactory
 ├─ com/example/risk/spi/
 │    ├─ RiskRuleFactory.java       // SPI接口
 │    ├─ ConsumerLoanFactory.java   // 业务线1实现
 │    ├─ CarLoanFactory.java        // 业务线2实现
 ├─ com/example/risk/spring/
 │    ├─ RiskRuleFactoryRegistry.java
 │    ├─ RiskControlAutoConfiguration.java
 │    ├─ DynamicRiskControlService.java

6.1.3. 示例代码

SPI接口定义(RiskRuleFactory

复制代码
package com.example.risk.spi;

public interface RiskRuleFactory {

    // 返回业务线标识,如 "consumerLoan"
    String getBizType(); 

    void evaluateUserProfile(String userId);
    void evaluateFraud(String userId);
    void evaluateCreditLimit(String userId);
}

SPI实现(以消费贷为例)

复制代码
package com.example.risk.spi;

public class ConsumerLoanFactory implements RiskRuleFactory {

    @Override
    public String getBizType() {
        return "consumerLoan";
    }

    @Override
    public void evaluateUserProfile(String userId) {
        System.out.println("消费贷用户画像评估: " + userId);
    }

    @Override
    public void evaluateFraud(String userId) {
        System.out.println("消费贷反欺诈评估: " + userId);
    }

    @Override
    public void evaluateCreditLimit(String userId) {
        System.out.println("消费贷授信额度评估: " + userId);
    }
}

同理实现 CarLoanFactory 等其他业务线工厂。

SPI配置文件:文件路径:src/main/resources/META-INF/services/com.example.risk.spi.RiskRuleFactory内容(每行一个实现类全限定名):

复制代码
com.example.risk.spi.ConsumerLoanFactory
com.example.risk.spi.CarLoanFactory

工厂注册中心(Spring Bean)

复制代码
package com.example.risk.spring;

import com.example.risk.spi.RiskRuleFactory;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.*;

@Component
public class RiskRuleFactoryRegistry {

    private final Map<String, RiskRuleFactory> factoryMap = new HashMap<>();

    @PostConstruct
    public void init() {
        ServiceLoader<RiskRuleFactory> loader = ServiceLoader.load(RiskRuleFactory.class);
        for (RiskRuleFactory factory : loader) {
            factoryMap.put(factory.getBizType(), factory);
            System.out.println("加载风控工厂:" + factory.getBizType());
        }
    }

    public RiskRuleFactory getFactory(String bizType) {
        RiskRuleFactory factory = factoryMap.get(bizType);
        if (factory == null) {
            throw new IllegalArgumentException("不支持的业务线: " + bizType);
        }
        return factory;
    }
}

Spring Boot 自动配置类

复制代码
package com.example.risk.spring;

import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@ConditionalOnProperty(prefix = "risk.control", name = "enabled", havingValue = "true", matchIfMissing = true)
public class RiskControlAutoConfiguration {

    @Bean
    public RiskRuleFactoryRegistry riskRuleFactoryRegistry() {
        return new RiskRuleFactoryRegistry();
    }

    @Bean
    public DynamicRiskControlService dynamicRiskControlService() {
        return new DynamicRiskControlService();
    }
}

动态风控服务

复制代码
package com.example.risk.spring;

import com.example.risk.spi.RiskRuleFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

@Service
public class DynamicRiskControlService {

    @Value("${risk.control.bizType:consumerLoan}")
    private String bizType;

    @Autowired
    private RiskRuleFactoryRegistry registry;

    public void doEvaluate(String userId) {
        RiskRuleFactory factory = registry.getFactory(bizType);
        factory.evaluateUserProfile(userId);
        factory.evaluateFraud(userId);
        factory.evaluateCreditLimit(userId);
    }
}

6.1.4. SPI+抽象工厂设计模式总结

  1. 扩展业务线 :只需实现 RiskRuleFactory 接口,并在 SPI 文件里声明实现类,打包成 jar 加入项目。
  2. 动态切换业务线 :修改配置 risk.control.bizType=carLoan 即可切换到车贷业务线风控。
  3. 热插拔:新的业务线可以单独打包成 jar 通过 SPI 机制自动被加载。

|-----------|----------------------|
| 优点 | 说明 |
| 插件式扩展 | 新增业务线无需改动核心代码,符合开闭原则 |
| Spring 集成 | 自动装配,减少手动配置 |
| 配置驱动 | 通过配置灵活切换业务逻辑 |
| 动态加载 | SPI 支持运行时加载多个实现 |

博文参考

相关推荐
杨充18 小时前
10.可测试性实战设计
设计模式·开源·代码规范
杨充19 小时前
9.重构十二式的实战
设计模式·开源·代码规范
杨充19 小时前
6.设计原则的全景图
设计模式·开源·全栈
杨充19 小时前
2.面向对象的特性
设计模式
杨充19 小时前
7.SOLID原则案例汇
设计模式·开源·全栈
杨充19 小时前
8.反模式与坏味道
设计模式·开源·代码规范
杨充19 小时前
3.接口vs抽象类比较
设计模式
咖啡八杯1 天前
文法、BNF与AST
java·设计模式·解释器模式·ast·文法
咖啡八杯1 天前
GoF设计模式——解释器模式
java·后端·spring·设计模式