策略模式与工厂模式的黄金组合:从设计到实战

发布于:2025-06-28 ⋅ 阅读:(14) ⋅ 点赞:(0)

策略模式和工厂模式是软件开发中最常用的两种设计模式,当它们结合使用时,能产生1+1>2的效果。本文将通过实际案例,阐述这两种模式的协同应用,让代码架构更优雅、可维护性更强。

一、为什么需要组合使用?

单独使用的痛点
  • 策略模式:客户端需要知道所有策略类,并手动创建策略实例
  • 工厂模式:单独使用时主要解决对象创建问题,不涉及算法切换
组合后的优势
  1. 彻底解耦:客户端无需知道策略类的存在和创建方式
  2. 一键切换:通过工厂统一管理策略实例,实现算法动态切换
  3. 集中管理:策略的注册、创建、缓存集中在工厂类,便于维护

二、核心实现:支付系统案例

1. 策略接口定义(Strategy)
// 支付策略接口
public interface PaymentStrategy {
    String pay(double amount);
    String getStrategyName();
}
2. 具体策略实现(Concrete Strategy)
// 支付宝支付策略
public class AlipayStrategy implements PaymentStrategy {
    @Override
    public String pay(double amount) {
        return "支付宝支付" + amount + "元,订单号:ALIPAY-" + System.currentTimeMillis();
    }
    @Override public String getStrategyName() { return "支付宝"; }
}

// 微信支付策略
public class WechatPayStrategy implements PaymentStrategy {
    @Override
    public String pay(double amount) {
        return "微信支付" + amount + "元,订单号:WECHAT-" + System.currentTimeMillis();
    }
    @Override public String getStrategyName() { return "微信支付"; }
}
3. 工厂模式实现(Factory)
public class PaymentStrategyFactory {
    // 策略缓存(单例模式+工厂模式)
    private static final Map<String, PaymentStrategy> STRATEGY_CACHE = new HashMap<>();
    private static final PaymentStrategyFactory INSTANCE = new PaymentStrategyFactory();
    
    private PaymentStrategyFactory() {
        // 注册所有策略(可从配置文件加载)
        registerStrategy("ALIPAY", new AlipayStrategy());
        registerStrategy("WECHAT", new WechatPayStrategy());
    }
    
    // 注册策略
    public void registerStrategy(String type, PaymentStrategy strategy) {
        STRATEGY_CACHE.put(type, strategy);
    }
    
    // 获取策略(工厂核心方法)
    public PaymentStrategy getStrategy(String type) {
        if (!STRATEGY_CACHE.containsKey(type)) {
            throw new IllegalArgumentException("不支持的支付方式:" + type);
        }
        return STRATEGY_CACHE.get(type);
    }
    
    // 获取工厂实例(单例)
    public static PaymentStrategyFactory getInstance() {
        return INSTANCE;
    }
}
4. 上下文类(Context)

(这里通过Context调用工厂还是非常有必要的,可以参考另外一篇:工厂模式中使用Map管理策略实例时,为何仍需要Context?

public class PaymentContext {
    private final PaymentStrategyFactory factory;
    private PaymentStrategy strategy;
    
    public PaymentContext(PaymentStrategyFactory factory) {
        this.factory = factory;
    }
    
    // 通过工厂设置策略
    public void setStrategy(String type) {
        this.strategy = factory.getStrategy(type);
    }
    
    // 执行支付
    public String executePayment(double amount) {
        return strategy.pay(amount);
    }
}
5. 客户端调用(Client)
public class Client {
    public static void main(String[] args) {
        // 获取工厂实例
        PaymentStrategyFactory factory = PaymentStrategyFactory.getInstance();
        // 创建上下文并传入工厂
        PaymentContext context = new PaymentContext(factory);
        
        // 使用支付宝支付
        context.setStrategy("ALIPAY");
        String result = context.executePayment(299.5);
        System.out.println(result);
        
        // 动态切换为微信支付
        context.setStrategy("WECHAT");
        result = context.executePayment(19.9);
        System.out.println(result);
    }
}

三、组合模式的类图解析

1
n
1
1
1
1
PaymentStrategy
+pay(amount: double)
+getStrategyName()
AlipayStrategy
+pay(amount: double)
+getStrategyName()
WechatPayStrategy
+pay(amount: double)
+getStrategyName()
PaymentStrategyFactory
-STRATEGY_CACHE: Map
-INSTANCE: PaymentStrategyFactory
+registerStrategy(type: String, strategy: PaymentStrategy)
+getStrategy(type: String)
+getInstance()
PaymentContext
-factory: PaymentStrategyFactory
-strategy: PaymentStrategy
+setStrategy(type: String)
+executePayment(amount: double)

核心关系

  1. 工厂类创建并管理策略实例
  2. 上下文类通过工厂获取策略
  3. 客户端只需与上下文和工厂交互,无需接触具体策略类

四、组合模式的优势:比单独使用强在哪?

1. 客户端代码简化对比

单独使用策略模式(需要手动创建策略)

// 客户端需要知道具体策略类并手动创建
PaymentContext context = new PaymentContext(new AlipayStrategy());

结合工厂模式(通过工厂获取策略)

// 客户端无需知道策略类,只需传入类型
context.setStrategy("ALIPAY");
2. 策略管理集中化
  • 策略注册、创建、缓存都在工厂类中完成
  • 支持策略的热插拔(如从配置文件动态加载策略)
3. 支持高级特性
  • 策略实例缓存(避免重复创建)
  • 策略版本管理(如支付宝策略升级时不影响客户端)
  • 策略权限控制(通过工厂限制可用策略)

五、高级应用:策略工厂的扩展实现

1. 枚举策略工厂(更简洁的实现)
public enum PaymentStrategyEnum {
    ALIPAY(new AlipayStrategy()),
    WECHAT(new WechatPayStrategy());
    
    private final PaymentStrategy strategy;
    PaymentStrategyEnum(PaymentStrategy strategy) {
        this.strategy = strategy;
    }
    
    public PaymentStrategy getStrategy() {
        return strategy;
    }
    
    // 通过类型获取策略
    public static PaymentStrategy getStrategy(String type) {
        for (PaymentStrategyEnum e : values()) {
            if (e.name().equals(type)) {
                return e.strategy;
            }
        }
        throw new IllegalArgumentException("不支持的策略:" + type);
    }
}

// 客户端调用
PaymentStrategy strategy = PaymentStrategyEnum.getStrategy("ALIPAY");
2. 基于注解的策略工厂(自动化注册)
// 策略注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Strategy {
    String value();
}

// 策略实现类
@Strategy("ALIPAY")
public class AlipayStrategy implements PaymentStrategy { /*...*/ }

// 工厂类(通过反射自动注册策略)
public class AutoRegisterStrategyFactory {
    private static final Map<String, PaymentStrategy> STRATEGY_MAP = new HashMap<>();
    
    static {
        // 扫描所有带@Strategy注解的类并注册
        List<Class<?>> strategyClasses = ClassScanner.scan("com.example.strategy");
        for (Class<?> clazz : strategyClasses) {
            if (clazz.isAnnotationPresent(Strategy.class)) {
                Strategy annotation = clazz.getAnnotation(Strategy.class);
                try {
                    PaymentStrategy strategy = (PaymentStrategy) clazz.getDeclaredConstructor().newInstance();
                    STRATEGY_MAP.put(annotation.value(), strategy);
                } catch (Exception e) {
                    throw new RuntimeException("策略注册失败", e);
                }
            }
        }
    }
    // ... 获取策略方法
}

六、实战场景:电商平台的策略组合应用

场景描述

电商平台需要实现:

  1. 多种支付策略(支付宝、微信、银联)
  2. 多种促销策略(满减、打折、优惠券)
  3. 多种配送策略(普通快递、加急快递、自提)
组合模式架构
客户端层
上下文层
策略工厂层
创建
创建
创建
OrderService
OrderContext
支付策略
PaymentStrategyFactory
促销策略
PromotionStrategyFactory
配送策略
DeliveryStrategyFactory

优势

  • 订单处理逻辑与具体策略解耦
  • 新增支付/促销/配送策略时无需修改订单核心代码
  • 工厂类可统一处理策略的权限控制、日志记录等横切关注点

七、组合模式的注意事项

  1. 策略类型的一致性

    • 工厂返回的策略必须实现同一接口,避免类型错误
    • 可通过泛型约束策略类型:
      public interface Strategy<T> { /*...*/ }
      public class StrategyFactory<T extends Strategy> { /*...*/ }
      
  2. 策略实例的线程安全性

    • 若策略是无状态的(如支付策略),可共享同一个实例
    • 若策略有状态,需为每个上下文创建独立实例
  3. 策略版本控制

    • 可在工厂中实现策略的版本管理,如:
      // 获取指定版本的策略
      public PaymentStrategy getStrategy(String type, int version) { /*...*/ }
      

八、总结:策略+工厂的核心价值

这两种模式的组合遵循了以下设计原则:

  • 开闭原则:新增策略无需修改工厂和上下文
  • 依赖倒置原则:客户端依赖抽象(策略接口)而非具体实现
  • 单一职责原则:策略类专注算法实现,工厂类专注对象创建

在实际开发中,如果遇到以下场景时,可考虑使用策略+工厂的组合模式:

  1. 系统中有多个算法族,且需要动态切换
  2. 希望将算法的创建和使用分离
  3. 避免在客户端代码中出现大量策略类的实例化逻辑

通过这种组合,可以构建出更加灵活、可扩展的系统架构,让代码在面对需求变化时更加从容。


网站公告

今日签到

点亮在社区的每一天
去签到