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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/86569.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/86569.shtml
英文地址,请注明出处:http://en.pswp.cn/bicheng/86569.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SAP PP模块与MM模块作用详解

SAP PP模块与MM模块作用详解 一、PP模块&#xff08;Production Planning&#xff09;—— 生产计划与执行中枢 核心作用&#xff1a;将销售需求转化为可执行的生产指令&#xff0c;管控从计划到完工的全过程。 关键功能 功能说明业务价值主数据管理维护BOM&#xff08;物料…

Linux tcp_info:监控TCP连接的秘密武器

深入解析 Linux tcp_info&#xff1a;TCP 状态的实时监控利器 在开发和运维网络服务时&#xff0c;我们常常遇到这些问题&#xff1a; 我的 TCP 连接为什么速度慢&#xff1f;是发生了重传&#xff0c;还是窗口太小&#xff1f;拥塞控制到底有没有生效&#xff1f; 这些问题…

CVE-2015-5531源码分析与漏洞复现(Elasticsearch目录遍历漏洞)

概述 漏洞名称&#xff1a;Elasticsearch 快照API目录遍历漏洞 CVE 编号&#xff1a;CVE-2015-5531 CVSS 评分&#xff1a;7.5 影响版本&#xff1a; Elasticsearch 1.0.0–1.6.0&#xff08;1.5.1及以前版本无需配置即可触发&#xff1b;1.5.2–1.6.0需配置path.repo&#xf…

HexHub开发运维利器Database, Docker, SSH, SFTP

支持隧道&#xff0c;SFTP&#xff0c;X11转发&#xff0c;跳板机&#xff0c;分屏广播输入&#xff0c;LRZSZ&#xff0c;TRZSZ&#xff0c;SCP 分屏广播输入 管理多台服务器&#xff0c;更快一步 支持多种文件传输协议 支持跨服务器文件传输&#xff0c;使用复制粘贴即可进…

2025年教育、心理健康与信息管理国际会议(EMHIM 2025)

2025 2nd International Conference on Education, Mental Health, and Information Management 一、大会信息 会议简称&#xff1a;EMHIM 2025 大会地点&#xff1a;中国三亚 收录检索&#xff1a;提交Ei Compendex,CPCI,CNKI,Google Scholar等 二、会议简介 第二届教…

数字孪生技术为UI前端注入新活力:实现智能化交互新体验

hello宝子们...我们是艾斯视觉擅长ui设计、前端开发、数字孪生、大数据、三维建模、三维动画10年经验!希望我的分享能帮助到您!如需帮助可以评论关注私信我们一起探讨!致敬感谢感恩! 在数字化转型的深水区&#xff0c;数字孪生技术正以破竹之势重构 UI 前端的技术逻辑与交互范式…

组件协作模式

目录 “组件协作”模式模板方法模式动机模式定义结构要点总结 “组件协作”模式 现代软件专业分工之后的第一个结果是“框架与应用程序的划分”。“组件协作”模式通过晚期绑定&#xff0c;实现框架与应用程序之间的松耦合&#xff0c;是二者之间协作时常用的模式。典型模式&a…

Docker 运行RAGFlow 搭建RAG知识库

借鉴视频&#xff1a;DeepSeek 10分钟完全本地部署 保姆级教程 断网运行 无惧隐私威胁 大语言模型 CPU GPU 混合推理32B轻松本地部署&#xff01;DeepSeek模拟王者&#xff01;&#xff01;_哔哩哔哩_bilibili 借鉴博客&#xff1a;RAGFlow搭建全攻略&#xff1a;从入门到精通…

python编写脚本每月1号和15号执行一次将TRX是否强更发送到钉钉

编写脚本 import requests import json import time import hmac import hashlib import base64 import urllib.parse# 1. 配置钉钉机器人 webhook "https://oapi.dingtalk.com/robot/send?access_tokenXXXXXX" secret "XXXXXXXX" # 如果没有加签验…

Linux-系统管理

[rootlocalhost ~]# lscpu //查看cpu [rootlocalhost etc]# cat /etc/redhat-release //查看当前目录的版本信息 [rootlocalhost ~]# ifconfig //查看当前激活的网卡信息 [rootlocalhost ~]# ifconfig ens33 192.168.1.10 //给网卡配置临时地址 [rootlocalhost ~]# hostnam…

【Spring】系统化的 Spring Boot 全栈学习教程,涵盖基础配置、核心功能、进阶实战及文档自动生成

这里写目录标题 &#x1f6e0;️ **一、环境搭建与项目创建**1. 开发环境准备2. 创建第一个项目&#xff08;Spring Initializr&#xff09; &#x1f680; **二、核心功能开发**1. RESTful API 开发2. 数据持久化&#xff08;Spring Data JPA&#xff09;3. 配置文件多环境切换…

Discrete Audio Tokens: More Than a Survey

文章目录 模型设计的考虑量化的方式&#xff1a;比特率&#xff1a;Fixed vs. Adaptive Bitrate码本内容设计的考虑Streamability. 模型评估Reconstruction Evaluation and Complexity Analysis.识别和生成任务&#xff08;SE, SR)Acoustic Language Modeling.Music Generation…

设计在线教育项目核心数据库表

1 在线教育项目核心数据库表设计-ER图 简介&#xff1a;设计在线教育的核心库表结构 在线教育站点速览 xdclass.net ER图知识回顾&#xff1a; 实体对象&#xff1a;矩形属性&#xff1a;椭圆关系&#xff1a;菱形 核心库表 videochapterepisodeuservideo_ordervideo_banner…

【音视频】Ubuntu下配置ffmpeg库

一、下载预编译的库 在github上可以找到编译好的ffmpeg&#xff0c;多个版本的都有&#xff0c;这里我下载ffmpeg编译好的动态库 仓库链接&#xff1a;(https://github.com/BtbN/FFmpeg-Builds/releases 下载后解压得到 二、配置环境变量 打开.bashrc配置文件&#xff0c;添…

equine在神经网络中建立量化不确定性

​一、软件介绍 文末提供程序和源码下载 众所周知&#xff0c;用于监督标记问题的深度神经网络 &#xff08;DNN&#xff09; 可以在各种学习任务中产生准确的结果。但是&#xff0c;当准确性是唯一目标时&#xff0c;DNN 经常会做出过于自信的预测&#xff0c;并且无论测试数…

C++动态链接库之非托管封装Invoke,供C#/C++ 等编程语言使用,小白教程——C++动态链接库(一)

目录&#xff1a; 一、前言及背景1.1需求描述1.2应用背景 二、编程基础知识2.1非托管方式交互逻辑2.2该方式下C 与C# 数据转换对应2.3VS工程下的注意点2.4C封装接口2.4.1 __declspec(dllexport) 方式2.4.2 .def 文件方式2.4.3结合使用&#xff08;高级&#xff09; 2.5C# 封装接…

消息队列的网络模型详解:IO多路复用、Reactor模型、零拷贝

文章目录 一、消息队列的网路模型拟解决问题单个请求性能优化1. 编解码速度2. 网络模块处理速度 并发请求性能优化1. 高效的连接管理2. 快速处理高并发请求3. 大流量场景处理 二、一些技术基础知识1. 基于多路复用技术管理 TCP 连接&#xff08;提高性能&#xff09;&#xff0…

【生成模型】【模型介绍】(一)视频生成Wan2.1速度质量简单评测

基础模型&#xff1a;FramePack https://github.com/kijai/ComfyUI-FramePackWrapper huggingface-cli download Comfy-Org/HunyuanVideo_repackaged --local-dir Comfy-Org/HunyuanVideo_repackaged --resume-download huggingface-cli download Comfy-Org/sigclip_vision_3…

微信小程序之滑块scroll-view

我们要做的东西&#xff1a; 滑块的视频 我们先做个基本的图片和文字(wxm;)&#xff1a; <scroll-view><view class"scrollItem"><image src"https://bkimg.cdn.bcebos.com/pic/fc1f4134970a304e251fd88e8191b086c9177f3ef634?x-bce-processim…

如何写出优秀的单元测试?

&#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 写出优秀的单元测试需要考虑以下几个方面&#xff1a; 1. 测试用例设计 测试用例应该覆盖被测试代码的不同场景和边界情况&#xff0c;以尽可能发现潜在的问题。…