当前位置: 首页 > news >正文

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

策略模式和工厂模式是软件开发中最常用的两种设计模式,当它们结合使用时,能产生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 {@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
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. 避免在客户端代码中出现大量策略类的实例化逻辑

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

相关文章:

  • C++并发编程-4.unique_lock,共享锁和递归锁
  • 关于Kotlin与Java的思考
  • 通过ETL从MySQL同步到GaussDB
  • Linux工作常用命令记录
  • Android开发获取视图组件的findViewById,kotlin-android-extensions,ViewBinding三种详解
  • PyWavelets
  • 分布式系统ID生成方案深度解析:雪花算法 vs UUID vs 其他主流方案
  • 航天VR赋能,无人机总测实验舱开启高效新篇​
  • 鸿蒙OS开发IoT控制应用:从入门到实践
  • 基于JavaWeb的校园失物招领系统设计与实现
  • 机器学习2——贝叶斯理论下
  • 概述-2-MySQL安装及启动-1-Dcoker安装MySQL
  • 那些不应该的优化
  • Hall 定理 学习笔记
  • 【Redis】解码Redis中的list类型,基本命令,内部编码方式以及适用的场景
  • Ai大模型 - ocr图像识别形成结构化数据(pp-ocr+nlp结合) 以及训练微调实现方案(初稿)
  • Prompt Engineering For LLMs
  • 【Linux基础知识系列】第三十二篇 - Shell 历史与命令编辑
  • eSearch识屏 · 搜索 v15.0.1 官方版
  • 使用 Vcpkg 安装 Qt 时的常见问题与解决方法