策略模式和工厂模式是软件开发中最常用的两种设计模式,当它们结合使用时,能产生1+1>2的效果。本文将通过实际案例,阐述这两种模式的协同应用,让代码架构更优雅、可维护性更强。
一、为什么需要组合使用?
单独使用的痛点
- 策略模式:客户端需要知道所有策略类,并手动创建策略实例
- 工厂模式:单独使用时主要解决对象创建问题,不涉及算法切换
组合后的优势
- 彻底解耦:客户端无需知道策略类的存在和创建方式
- 一键切换:通过工厂统一管理策略实例,实现算法动态切换
- 集中管理:策略的注册、创建、缓存集中在工厂类,便于维护
二、核心实现:支付系统案例
1. 策略接口定义(Strategy)
// 支付策略接口
public interface PaymentStrategy {String pay(double amount);String getStrategyName();
}
2. 具体策略实现(Concrete Strategy)
// 支付宝支付策略
public class AlipayStrategy implements PaymentStrategy {@Overridepublic String pay(double amount) {return "支付宝支付" + amount + "元,订单号:ALIPAY-" + System.currentTimeMillis();}@Override public String getStrategyName() { return "支付宝"; }
}// 微信支付策略
public class WechatPayStrategy implements PaymentStrategy {@Overridepublic 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. 客户端代码简化对比
单独使用策略模式(需要手动创建策略):
// 客户端需要知道具体策略类并手动创建
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);}}}}// ... 获取策略方法
}
六、实战场景:电商平台的策略组合应用
场景描述
电商平台需要实现:
- 多种支付策略(支付宝、微信、银联)
- 多种促销策略(满减、打折、优惠券)
- 多种配送策略(普通快递、加急快递、自提)
组合模式架构
优势:
- 订单处理逻辑与具体策略解耦
- 新增支付/促销/配送策略时无需修改订单核心代码
- 工厂类可统一处理策略的权限控制、日志记录等横切关注点
七、组合模式的注意事项
-
策略类型的一致性:
- 工厂返回的策略必须实现同一接口,避免类型错误
- 可通过泛型约束策略类型:
public interface Strategy<T> { /*...*/ } public class StrategyFactory<T extends Strategy> { /*...*/ }
-
策略实例的线程安全性:
- 若策略是无状态的(如支付策略),可共享同一个实例
- 若策略有状态,需为每个上下文创建独立实例
-
策略版本控制:
- 可在工厂中实现策略的版本管理,如:
// 获取指定版本的策略 public PaymentStrategy getStrategy(String type, int version) { /*...*/ }
- 可在工厂中实现策略的版本管理,如:
八、总结:策略+工厂的核心价值
这两种模式的组合遵循了以下设计原则:
- 开闭原则:新增策略无需修改工厂和上下文
- 依赖倒置原则:客户端依赖抽象(策略接口)而非具体实现
- 单一职责原则:策略类专注算法实现,工厂类专注对象创建
在实际开发中,如果遇到以下场景时,可考虑使用策略+工厂的组合模式:
- 系统中有多个算法族,且需要动态切换
- 希望将算法的创建和使用分离
- 避免在客户端代码中出现大量策略类的实例化逻辑
通过这种组合,可以构建出更加灵活、可扩展的系统架构,让代码在面对需求变化时更加从容。