【设计模式】策略模式(Strategy Pattern)详解

devtools/2025/3/30 5:40:39/

策略模式(Strategy Pattern)详解

一、策略模式的定义

策略模式(Strategy Pattern)是一种行为型设计模式它定义了一组算法,将每个算法封装起来,并使它们可以相互替换,从而让算法的变化独立于使用它的客户(Client)。

换句话说,策略模式让一个类的行为或其算法可以在运行时更改,而不会影响使用该类的代码。


二、生活中的类比

想象一下,你去一家披萨店点披萨,他们提供了三种不同的切割方式:

  1. 正常切割(切成八块)
  2. 方块切割(切成小方块)
  3. 不切割(整块送上)

每种切割方式是一个策略,你可以在点餐时选择适合自己的方式。这意味着:

  • 披萨店(上下文,Context)不关心具体的切割方法,只需要使用一个策略接口来执行对应的切割方式。
  • 你可以随时更换切割策略,而不需要修改披萨店的代码。

三、策略模式的结构

策略模式主要由 3 个核心部分 组成:

组件作用
策略接口(Strategy)定义一组可以互相替换的算法接口。
具体策略(Concrete Strategy)实现策略接口的不同算法。
上下文(Context)负责与策略接口交互,并可以动态切换策略。
UML 类图
        +----------------------+|       Context        |  |----------------------|| strategy: Strategy   |   ---->  +----------------------+|----------------------|          |     Strategy         |  | setStrategy()        |          |----------------------|| executeStrategy()    |          | execute()            |+----------------------+          +----------------------+|                               ▲|                               ||         +----------------------------------------+|         |                    |                  |+--------------------+  +--------------------+  +--------------------+|  ConcreteStrategyA |  |  ConcreteStrategyB |  |  ConcreteStrategyC ||--------------------|  |--------------------|  |--------------------|| execute()          |  | execute()          |  | execute()          |+--------------------+  +--------------------+  +--------------------+

四、策略模式的 Python 实现

(1) 定义策略接口
from abc import ABC, abstractmethod# 1. 定义策略接口(抽象策略)
class Strategy(ABC):@abstractmethoddef execute(self, a, b):pass
(2) 实现具体策略
# 2. 具体策略A:加法
class AddStrategy(Strategy):def execute(self, a, b):return a + b# 3. 具体策略B:减法
class SubtractStrategy(Strategy):def execute(self, a, b):return a - b# 4. 具体策略C:乘法
class MultiplyStrategy(Strategy):def execute(self, a, b):return a * b
(3) 创建上下文并动态切换策略
# 5. 上下文类
class Context:def __init__(self, strategy: Strategy):self._strategy = strategy  # 初始化时指定策略def set_strategy(self, strategy: Strategy):"""动态更改策略"""self._strategy = strategydef execute_strategy(self, a, b):"""执行策略"""return self._strategy.execute(a, b)
(4) 测试策略模式
# 客户端代码
context = Context(AddStrategy())  # 初始使用加法策略
print("10 + 5 =", context.execute_strategy(10, 5))context.set_strategy(SubtractStrategy())  # 切换为减法策略
print("10 - 5 =", context.execute_strategy(10, 5))context.set_strategy(MultiplyStrategy())  # 切换为乘法策略
print("10 * 5 =", context.execute_strategy(10, 5))
(5) 输出结果
10 + 5 = 15
10 - 5 = 5
10 * 5 = 50

五、策略模式的优缺点

✅ 优点
  1. 符合开闭原则(Open-Closed Principle):可以新增新的策略,而不影响已有代码。
  2. 避免冗长的 if-else 语句:如果不使用策略模式,可能会有大量 if-else 逻辑判断。
  3. 支持动态切换:可以在运行时自由更换不同的算法,而不修改原有代码。
❌ 缺点
  1. 增加了代码复杂度:每个策略都需要定义一个类,当策略过多时,会导致类爆炸。
  2. 客户端需要了解不同策略:调用方需要知道有哪些可用策略,才能正确选择。

六、策略模式的应用场景

(1) 算法的可替换性
  • 计算税收(不同国家税率)
  • 支付方式(微信支付、支付宝、银行卡)
(2) 处理不同格式的文件
  • 解析 JSON、XML、CSV 文件时,使用不同的解析策略。
(3) AI/机器学习中的优化策略
  • 训练模型时,可以使用不同的优化算法(SGD、Adam、RMSprop)。

七、策略模式 vs 其他模式

设计模式作用适用场景
策略模式允许在运行时更换不同算法。当有多种算法可选,且希望避免 if-else 逻辑。
状态模式允许对象在不同状态下表现出不同行为。对象的行为依赖于状态变化,例如订单状态。
工厂模式负责创建对象,但不指定其具体实现。需要动态创建不同策略对象时,可结合策略模式使用。

八、总结

  • 策略模式核心思想: 定义一组算法,让它们可以互相替换,而不会影响客户端代码。
  • 适用于: 需要多个可替换算法,并希望避免 if-else 逻辑的场景。
  • 优点: 符合开闭原则,可动态切换策略,避免冗长 if-else
  • 缺点: 可能会增加类的数量,使代码更复杂。

为什么不用一个类,而是用策略模式

你提的这个问题很关键!乍一看,把不同的算法方法写在同一个类里,确实可以实现同样的功能。比如,我们可以直接在 Context 里写多个方法,而不是用多个策略类:

class Calculator:def add(self, a, b):return a + bdef subtract(self, a, b):return a - bdef multiply(self, a, b):return a * b

然后调用:

calc = Calculator()
print(calc.add(10, 5))
print(calc.subtract(10, 5))
print(calc.multiply(10, 5))

这看起来代码更简单,那么为什么我们还要使用策略模式呢?🤔


策略模式 vs. 在同一个类中写多个方法

方式优势劣势
把所有方法写在一个类里代码量少、容易理解不符合开闭原则,修改或添加算法时要改原始代码,不利于扩展
使用策略模式(多个类)代码更灵活、符合开闭原则,更易扩展初期代码量较多,需要额外的类

五大核心原因:为什么策略模式更优?

(1) 符合 “开闭原则”(OCP)

开闭原则(Open-Closed Principle,OCP):软件应该对扩展开放,对修改关闭。

假设你的 Calculator 需要增加 除法取余指数运算平方根 等方法,那么:

  • 如果所有方法都写在 Calculator,你必须不断修改它,破坏 OCP
  • 如果使用策略模式,你只需新增一个策略类,而不需要改动原来的代码。

示例:

  • 传统方式:

    class Calculator:def add(self, a, b): return a + bdef subtract(self, a, b): return a - bdef multiply(self, a, b): return a * bdef divide(self, a, b): return a / b  # 这里修改了原始类
    

    问题:

    • 你不得不改动 Calculator,如果这个类被很多地方用到,可能影响其他代码。
    • 代码耦合度变高,修改的风险变大。
  • 策略模式

    class DivideStrategy(Strategy):def execute(self, a, b): return a / b  # 只新增类,不改动原有代码
    

    好处:

    • 旧代码不需要改动,减少出错的风险!
    • 代码更模块化,每个策略独立,更易扩展

(2) 让代码更清晰,避免复杂的 if-else

如果不使用策略模式,你可能会写出大量的 if-else 语句

class Calculator:def execute(self, strategy, a, b):if strategy == "add":return a + belif strategy == "subtract":return a - belif strategy == "multiply":return a * belse:raise ValueError("Unknown strategy")

问题:

  • 随着策略增加,if-else 会变得越来越长,可读性下降。
  • 每次增加新策略,都要改 execute 方法,代码耦合度高。

而使用策略模式后,代码变得清晰且易维护

context = Context(AddStrategy())  # 直接使用策略对象
result = context.execute_strategy(10, 5)  # 运行时切换策略
  • 没有 if-else,可读性更强!
  • 新增算法时,不需要改 Context,只需添加新的 Strategy 类。

(3) 运行时动态切换策略

假设你在运行时需要更换算法:

  • 单类方案:你必须写 if-else 逻辑,或者调用不同的方法。
  • 策略模式:你可以动态传入不同的策略对象,无需 if-else

示例:

context = Context(AddStrategy())  # 初始使用加法
context.execute_strategy(10, 5)  # 10 + 5 = 15context.set_strategy(SubtractStrategy())  # 运行时切换成减法
context.execute_strategy(10, 5)  # 10 - 5 = 5
  • 运行时随意切换算法,而不需要 if-else
  • 新增算法时不影响原代码,降低耦合度。

(4) 代码更容易测试

  • 单类方案:如果 Calculator 里包含 10+ 种运算方法,每次测试它,你需要测试整个 Calculator 类,容易受其他方法影响。
  • 策略模式:每个策略独立,你可以单独测试每个 Strategy 类,不受其他策略影响。

示例(使用 PyTest 单测):

def test_add_strategy():strategy = AddStrategy()assert strategy.execute(10, 5) == 15def test_subtract_strategy():strategy = SubtractStrategy()assert strategy.execute(10, 5) == 5
  • 单独测试每个策略,而不是整个 Calculator 类。
  • 减少依赖关系,让测试更清晰。

(5) 代码更符合 SOLID 原则

设计原则策略模式如何符合
单一职责原则(SRP)每个 Strategy 只负责一个算法,职责清晰。
开闭原则(OCP)可以新增策略,而不修改原代码。
依赖倒置原则(DIP)Context 依赖于 Strategy 接口,而不是具体实现。

总结

方案优势劣势
单类 + 多方法代码短、简单不符合开闭原则,难以扩展,难以测试,耦合度高
策略模式符合 SOLID 原则、易扩展、易测试初期代码量较多

什么时候该用策略模式

当你有多个可变的算法(如支付方式、排序方法)时,策略模式是最佳选择!
如果你的方法固定、不会变化,直接写在一个类里更简单。


一句话总结

👉 如果你的代码需要多个可替换的算法,并且希望避免 if-else 代码膨胀,策略模式就是最好的选择! 🎯


http://www.ppmy.cn/devtools/171308.html

相关文章

Kafka 4.0 发布,彻底移除 ZooKeeper!

最近 Apache Kafka 4.0 版本发布,这是 Kafka 的一个重要里程碑。Kafka 移除 ZooKeeper 早在几年前就开始准备,4.0 版本是第一个可以完全不使用 ZooKeeper 的版本。 Kafka 4.0 默认允许在 KRaft 模式下,大大简化了集群的部署和管理&#xff0…

STM32基础教程——定时器

前言 TIM定时器(Timer):STM32的TIM定时器是一种功能强大的外设模块,通过时基单元(包含预分频器、计数器和自动重载寄存器)实现精准定时和计数功能。其核心原理是:内部时钟(CK_INT)或…

python工厂模式

工厂模式简介 工厂模式是一种创建对象的设计模式,它将对象的创建和使用分离。其核心思想是通过一个工厂类来负责创建对象,而不是在客户端代码中直接实例化对象。这样做的好处有: 降低耦合度:客户端代码不需要知道具体的对象创建细…

【工程记录】QwQ-32b 8bit量化部署教程(vLLM | 缓解复读)

文章目录 写在前面1. 环境配置2. 下载QwQ-32b 8bit量化模型3. 使用vLLM本地推理 写在前面 仅作个人学习记录用。本文记录QwQ-32b 8bit量化模型的部署的详细方法。 1. 环境配置 以下环境经测试无bug(Deepseek R1用这个环境也能直接跑): gp…

基于大模型预测的初治菌阳肺结核诊疗方案研究报告

目录 一、引言 1.1 研究背景与意义 1.2 研究目的 二、初治菌阳肺结核概述 2.1 疾病定义与病理机制 2.2 流行病学特征 2.3 传统诊疗方法与局限性 三、大模型在初治菌阳肺结核预测中的应用原理 3.1 大模型技术简介 3.2 数据收集与预处理 3.3 模型构建与训练 3.4 模型…

MATLAB中构建模式表达式

目录 构建简单模式 边界模式 分步构建复杂模式 组织模式显示 模式是帮助搜索和修改文本的工具。与正则表达式类似,模式定义匹配文本的规则。模式可与文本搜索函数(如 contains、matches 和 extract)结合使用来指定这些函数作用于文本的哪…

【TCP/IP、HTTP等网络协议】

网络协议概述 网络协议是计算机网络中用于规范数据传输的一组规则和标准。它们定义了数据的格式、传输方式、错误检测和纠正机制等。不同的协议在不同的网络层次中发挥作用,确保数据能够在复杂的网络环境中可靠地传输。 1. TCP/IP 协议 TCP/IP(Transm…

数据结构--二叉排序树

一、二叉排序树的定义 二叉排序树&#xff0c;又称二叉查找树。 性质&#xff1a; 左子树结点值<根结点值<右子树结点值&#xff08;进行中序遍历&#xff0c;可以得到一个递增的有序序列&#xff09; 二、查找操作 利用二叉排序树的性质&#xff0c;如果树空&#xff0c…