工厂模式中使用Map管理策略实例时,为何仍需要Context?

看这篇文章前,可以先了解一下:策略模式与工厂模式的黄金组合:从设计到实战

一、核心矛盾:创建职责与调用职责的分离问题

当使用Map管理策略实例时(如Map strategyMap),工厂确实能高效获取策略实例,但这仅解决了**“策略从哪里来"的问题。而策略的"如何使用”**仍面临以下挑战:

  1. 上下文逻辑碎片化:策略调用前后的公共逻辑(如参数校验、结果处理)会散落在客户端代码中
  2. 调用流程不一致:不同客户端可能以不同方式调用策略,导致流程混乱
  3. 横切关注点泄露:非核心业务逻辑(日志、事务)污染策略接口
二、Context不可替代的三大核心价值
1. 上下文逻辑的集中封装
// 无Context时的客户端调用(反模式)
public class Client {
    private final StrategyFactory factory = new StrategyFactory();
    
    public void process(Order order) {
        // 1. 前置校验(应属于上下文逻辑)
        if (order.getAmount() <= 0) {
            throw new IllegalArgumentException("订单金额无效");
        }
        
        // 2. 从工厂获取策略
        PaymentStrategy strategy = factory.getStrategy(order.getPayType());
        
        // 3. 执行策略(核心逻辑)
        boolean result = strategy.pay(order);
        
        // 4. 后置处理(应属于上下文逻辑)
        if (result) {
            order.setStatus(OrderStatus.PAID);
            log.info("支付成功,订单ID:" + order.getId());
        } else {
            retryPayment(order); // 重试逻辑
        }
    }
}
2. 统一的策略调用接口
// 引入Context后的标准调用流程
public class PaymentContext {
    private final StrategyFactory factory;
    
    public PaymentContext(StrategyFactory factory) {
        this.factory = factory;
    }
    
    public PaymentResult execute(Order order) {
        // 1. 统一前置校验
        validateOrder(order);
        
        try {
            // 2. 从工厂获取策略
            PaymentStrategy strategy = factory.getStrategy(order.getPayType());
            
            // 3. 执行策略核心逻辑
            boolean payResult = strategy.pay(order);
            
            // 4. 统一后置处理
            return handlePaymentResult(order, payResult);
        } catch (Exception e) {
            // 5. 统一异常处理
            log.error("支付异常", e);
            return PaymentResult.failure(e.getMessage());
        }
    }
    
    // 上下文相关的辅助方法(封装细节)
    private void validateOrder(Order order) { /*...*/ }
    private PaymentResult handlePaymentResult(Order order, boolean result) { /*...*/ }
}
3. 客户端代码的简化与解耦
// 客户端只需与Context交互
public class Client {
    private final PaymentContext context;
    
    public Client() {
        StrategyFactory factory = new StrategyFactory();
        this.context = new PaymentContext(factory);
    }
    
    public void processOrder(Order order) {
        PaymentResult result = context.execute(order);
        // 仅处理业务结果,无需关心调用细节
        if (result.isSuccess()) {
            notifyCustomer(order);
        }
    }
}
三、职责边界对比:工厂 vs Context vs 客户端
组件 核心职责 不可替代的原因
工厂 策略实例的创建与注册(如Map管理) 封装对象创建逻辑,符合DRY原则
Context 策略的调用流程控制与上下文处理 封装公共逻辑,避免客户端重复代码
客户端 业务决策与结果处理 聚焦核心业务,不关心技术细节
四、UML协作图:三者的交互流程
客户端 Context 工厂 策略 调用execute(order) 执行前置校验 获取策略实例 从Map获取策略 返回策略实例 调用策略方法 执行核心逻辑 返回执行结果 执行后置处理 返回处理结果 客户端 Context 工厂 策略
五、典型反模式:当省略Context时的问题
  1. 代码重复:每个客户端都需要编写相似的校验/日志代码
  2. 维护困难:修改公共逻辑时需更新所有客户端
  3. 职责混乱:工厂被强行加入调用逻辑,违反单一职责
  4. 测试复杂:客户端代码包含过多技术细节,难以单元测试
六、总结:Context的本质定位

工厂是"策略对象的仓库",而Context是"策略执行的引擎"。两者结合实现:

  • 技术细节封装:将策略创建、调用流程、异常处理等技术细节与业务逻辑分离
  • 流程标准化:确保所有策略以统一方式被调用,降低协作成本
  • 可扩展性增强:新增策略时,只需修改工厂注册逻辑,不影响调用流程

这种设计在开源框架中十分常见,例如:

  • Spring的TaskExecutor(Context)与TaskExecutorFactory(工厂)
  • MyBatis的Executor(Context)与ExecutorFactory(工厂)

通过合理分离创建职责与调用职责,系统将更符合"高内聚、低耦合"的设计原则。

你可能感兴趣的:(工厂模式中使用Map管理策略实例时,为何仍需要Context?)