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

策略模式(Strategy Pattern)详解

一、策略模式的定义

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

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


二、生活中的类比

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

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

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

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

三、策略模式的结构

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

组件 作用
策略接口(Strategy) 定义一组可以互相替换的算法接口。
具体策略(Concrete Strategy) 实现策略接口的不同算法。
上下文(Context) 负责与策略接口交互,并可以动态切换策略。

UML 类图

urm 复制代码
        +----------------------+
        |       Context        |  
        |----------------------|
        | strategy: Strategy   |   ---->  +----------------------+
        |----------------------|          |     Strategy         |  
        | setStrategy()        |          |----------------------|
        | executeStrategy()    |          | execute()            |
        +----------------------+          +----------------------+
                     |                               ▲
                     |                               |
                     |         +----------------------------------------+
                     |         |                    |                  |
        +--------------------+  +--------------------+  +--------------------+
        |  ConcreteStrategyA |  |  ConcreteStrategyB |  |  ConcreteStrategyC |
        |--------------------|  |--------------------|  |--------------------|
        | execute()          |  | execute()          |  | execute()          |
        +--------------------+  +--------------------+  +--------------------+

四、策略模式的 Python 实现

(1) 定义策略接口

python 复制代码
from abc import ABC, abstractmethod

# 1. 定义策略接口(抽象策略)
class Strategy(ABC):
    @abstractmethod
    def execute(self, a, b):
        pass

(2) 实现具体策略

python 复制代码
# 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) 创建上下文并动态切换策略

python 复制代码
# 5. 上下文类
class Context:
    def __init__(self, strategy: Strategy):
        self._strategy = strategy  # 初始化时指定策略

    def set_strategy(self, strategy: Strategy):
        """动态更改策略"""
        self._strategy = strategy

    def execute_strategy(self, a, b):
        """执行策略"""
        return self._strategy.execute(a, b)

(4) 测试策略模式

bash 复制代码
# 客户端代码
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) 输出结果

ini 复制代码
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 里写多个方法,而不是用多个策略类:

ruby 复制代码
class Calculator:
    def add(self, a, b):
        return a + b

    def subtract(self, a, b):
        return a - b

    def multiply(self, a, b):
        return a * b

然后调用:

scss 复制代码
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
  • 如果使用策略模式 ,你只需新增一个策略类,而不需要改动原来的代码。

示例:

  • 传统方式:

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

    问题:

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

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

    好处:

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

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

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

kotlin 复制代码
class Calculator:
    def execute(self, strategy, a, b):
        if strategy == "add":
            return a + b
        elif strategy == "subtract":
            return a - b
        elif strategy == "multiply":
            return a * b
        else:
            raise ValueError("Unknown strategy")

问题:

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

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

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

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

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

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

示例:

ini 复制代码
context = Context(AddStrategy())  # 初始使用加法
context.execute_strategy(10, 5)  # 10 + 5 = 15

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

(4) 代码更容易测试

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

示例(使用 PyTest 单测):

scss 复制代码
def test_add_strategy():
    strategy = AddStrategy()
    assert strategy.execute(10, 5) == 15

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

(5) 代码更符合 SOLID 原则

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

总结

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

什么时候该用策略模式?

当你有多个可变的算法(如支付方式、排序方法)时,策略模式是最佳选择!

如果你的方法固定、不会变化,直接写在一个类里更简单。


一句话总结

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

相关推荐
间彧10 小时前
Windows Server,如何使用WSFC+nginx实现集群故障转移
后端
间彧10 小时前
Nginx + Keepalived 实现高可用集群(Linux下)
后端
间彧10 小时前
在Kubernetes中如何部署高可用的Nginx Ingress Controller?
后端
间彧10 小时前
Ribbon负载均衡器和Nginx负载均衡器有什么区别
后端
间彧11 小时前
Nacos详解与项目实战
后端
间彧11 小时前
nginx、网关Gateway、Nacos、多个服务实例之间的数据链路详解
后端
间彧11 小时前
Nacos与Eureka在性能上有哪些具体差异?
后端
间彧11 小时前
详解Nacos健康状态监测机制
后端
间彧11 小时前
如何利用Nacos实现配置的灰度发布?
后端
毕业设计制作和分享11 小时前
springboot159基于springboot框架开发的景区民宿预约系统的设计与实现
java·spring boot·后端