Python 设计模式之策略模式
有勇气的牛排
15
Python
2025-08-01 22:22:56
Python 设计模式之 策略模式
1 前言
1.1 什么是策略模式?
策略模式(Strategy Pattern)是一种行为设计模式,定义一系列可互换的算法,把它们各自封装起来,并使它们可以相互替换,而不影响客户端的使用。
通俗讲:
- 有多个可选策略(算法),可以按需选择并动态切换。
- 客户端不关心用的是哪种策略,只要遵守接口就能用。
1.2 结构图(经典写法)
[Context上下文]
|
↓
[Strategy接口]←── [具体策略A]
[具体策略B]
- Context:上下文,持有一个策略对象。
- Strategy:抽象策略类(Python中通常是协议或接口)
- ConcreteStrategy:具体策略实现。
1.3 设计思路
传统写法(不适用策略模式)可能会用 if-else 判断:
if mode == "add":
result = a + b
else mode == "sub":
result = a - b
这样的问题是:
- 如果有新的算法,需要修改调用方法。
- 条件分支会越来越多,不利于维护。
策略模式的改进思路:
- 定义一个策略接口(Strategy)。
- 每个算法实现一个策略类,实现该接口。
- 在运行时选择策略对象。
1.4 常见场景
- 电商系统中不同支持防止(微信、支付宝、银行卡)。
- 数据排序时选择不同排序算法。
- 图片压缩时选择不同压缩方式。
- 机器学习模型中选择不同训练策略。
- 游戏中选择不同攻击行为。
1.5 优缺点
优点:
- 解耦算法选择逻辑和实现
- 新增策略类不影响已有代码(开放封闭原则)
- 可动态切换行为,扩展性强
- 消除了大量
if-else
语句
- 算法可以自由切换,符合开闭原则(OCP)。
缺点:
- 会增加类或函数数量,管理复杂
- 客户端需要知道所有策略,并主动选择
2 基本案例
2.1 面向对象版本
from typing import Protocol
class Strategy(Protocol):
def execute(self, a: int, b: int) -> int:
...
class AddStrategy:
def execute(self, a: int, b: int):
return a + b
class SubStrategy:
def execute(self, a: int, b: int):
return a - b
class MulStrategy:
def execute(self, a: int, b: int):
return a * b
class Calculator:
def __init__(self, strategy: Strategy):
self.strategy = strategy
def set_strategy(self, strategy: Strategy):
"""允许在运行时切换策略"""
self.strategy = strategy
def calculate(self, a: int, b: int) -> int:
return self.strategy.execute(a, b)
if __name__ == "__main__":
calc = Calculator(AddStrategy())
print("加法:", calc.calculate(5, 3))
calc.set_strategy(SubStrategy())
print("减法:", calc.calculate(5, 3))
calc.set_strategy(MulStrategy())
print("乘法:", calc.calculate(5, 3))
2.2 函数式(Pythonic)版
由于 Python 支持 函数作为一等对象,可以直接用函数替代策略类。
def add(a, b):
return a + b
def sub(a, b):
return a - b
def mul(a, b):
return a * b
class Calculator:
def __init__(self, strategy):
self.strategy = strategy
def set_strategy(self, strategy):
self.strategy = strategy
def calculate(self, a, b):
return self.strategy(a, b)
calc = Calculator(add)
print("加法:", calc.calculate(5, 3))
calc.set_strategy(sub)
print("减法:", calc.calculate(5, 3))
calc.set_strategy(mul)
print("乘法:", calc.calculate(5, 3))
优点:更简洁,符合 Python 风格。
缺点:没有类的结构,可能在大型系统中可读性稍差。
3 支付策略模式
from abc import ABC, abstractmethod
class PaymentStrategy(ABC):
@abstractmethod
def pay(self, amount: float) -> None:
pass
class AliPayStrategy(PaymentStrategy):
def pay(self, amount: float) -> bool:
print(f"[支付宝] 支付 {amount} 元成功")
return True
class WeChatPayStrategy(PaymentStrategy):
def pay(self, amount: float) -> bool:
print(f"[微信] 支付 {amount} 元成功")
return True
class BankCardPayStrategy(PaymentStrategy):
def pay(self, amount: float) -> bool:
print(f"[银行卡] 支付 {amount} 元成功")
return True
class PaymentContext:
def __init__(self, strategy: PaymentStrategy):
self.strategy = strategy
def execute_payment(self, amount: float) -> bool:
return self.strategy.pay(amount)
STRATEGY_MAP = {
"alipay": AliPayStrategy,
"wechat": WeChatPayStrategy,
"bank": BankCardPayStrategy,
}
def handle_payment_request(pay_type: str, amount: float):
strategy_class = STRATEGY_MAP.get(pay_type)
if not strategy_class:
raise ValueError(f"不支持的支付方式: {pay_type}")
context = PaymentContext(strategy_class())
return context.execute_payment(amount)
if __name__ == "__main__":
handle_payment_request("alipay", 100.0)
handle_payment_request("wechat", 200.0)
handle_payment_request("bank", 500.0)

<h1><a id="Python___0"></a>Python 设计模式之 策略模式</h1>
<h2><a id="1__2"></a>1 前言</h2>
<h3><a id="11__4"></a>1.1 什么是策略模式?</h3>
<p><strong>策略模式(Strategy Pattern)<strong>是一种</strong>行为设计模式</strong>,定义一系列可互换的算法,把它们各自封装起来,并使它们可以相互替换,而不影响客户端的使用。</p>
<p>通俗讲:</p>
<ul>
<li>有多个可选策略(算法),可以按需选择并动态切换。</li>
<li>客户端不关心用的是哪种策略,只要遵守接口就能用。</li>
</ul>
<h3><a id="12__13"></a>1.2 结构图(经典写法)</h3>
<pre><div class="hljs"><code class="lang-less"><span class="hljs-selector-attr">[Context上下文]</span>
|
↓
<span class="hljs-selector-attr">[Strategy接口]</span>←── <span class="hljs-selector-attr">[具体策略A]</span>
<span class="hljs-selector-attr">[具体策略B]</span>
</code></div></pre>
<ul>
<li>Context:上下文,持有一个策略对象。</li>
<li>Strategy:抽象策略类(Python中通常是协议或接口)</li>
<li>ConcreteStrategy:具体策略实现。</li>
</ul>
<h3><a id="13__27"></a>1.3 设计思路</h3>
<p>传统写法(不适用策略模式)可能会用 <strong>if-else</strong> 判断:</p>
<pre><div class="hljs"><code class="lang-python"><span class="hljs-keyword">if</span> mode == <span class="hljs-string">"add"</span>:
result = a + b
<span class="hljs-keyword">else</span> mode == <span class="hljs-string">"sub"</span>:
result = a - b
</code></div></pre>
<p>这样的问题是:</p>
<ul>
<li>如果有新的算法,需要修改调用方法。</li>
<li>条件分支会越来越多,不利于维护。</li>
</ul>
<p>策略模式的改进思路:</p>
<ul>
<li>定义一个<strong>策略接口</strong>(Strategy)。</li>
<li>每个算法实现一个策略类,实现该接口。</li>
<li>在运行时选择策略对象。</li>
</ul>
<h3><a id="14__49"></a>1.4 常见场景</h3>
<ul>
<li>电商系统中不同支持防止(微信、支付宝、银行卡)。</li>
<li>数据排序时选择不同排序算法。</li>
<li>图片压缩时选择不同压缩方式。</li>
<li>机器学习模型中选择不同训练策略。</li>
<li>游戏中选择不同攻击行为。</li>
</ul>
<h3><a id="15__57"></a>1.5 优缺点</h3>
<p>优点:</p>
<ul>
<li>解耦算法选择逻辑和实现</li>
<li>新增策略类不影响已有代码(开放封闭原则)</li>
<li>可动态切换行为,扩展性强</li>
<li>消除了大量 <code>if-else</code>语句</li>
<li>算法可以自由切换,符合<strong>开闭原则(OCP)</strong>。</li>
</ul>
<p>缺点:</p>
<ul>
<li>会增加类或函数数量,管理复杂</li>
<li>客户端需要知道所有策略,并主动选择</li>
</ul>
<h2><a id="2__74"></a>2 基本案例</h2>
<h3><a id="21__76"></a>2.1 面向对象版本</h3>
<pre><div class="hljs"><code class="lang-python"><span class="hljs-comment"># -*- coding: utf-8 -*-</span>
<span class="hljs-comment"># 策略解耦(使用Protocol 方便类型检查)</span>
<span class="hljs-keyword">from</span> typing <span class="hljs-keyword">import</span> Protocol
<span class="hljs-keyword">class</span> <span class="hljs-title class_">Strategy</span>(<span class="hljs-title class_ inherited__">Protocol</span>):
<span class="hljs-keyword">def</span> <span class="hljs-title function_">execute</span>(<span class="hljs-params">self, a: <span class="hljs-built_in">int</span>, b: <span class="hljs-built_in">int</span></span>) -> <span class="hljs-built_in">int</span>:
...
<span class="hljs-comment"># 具体策略实现</span>
<span class="hljs-keyword">class</span> <span class="hljs-title class_">AddStrategy</span>:
<span class="hljs-keyword">def</span> <span class="hljs-title function_">execute</span>(<span class="hljs-params">self, a: <span class="hljs-built_in">int</span>, b: <span class="hljs-built_in">int</span></span>):
<span class="hljs-keyword">return</span> a + b
<span class="hljs-keyword">class</span> <span class="hljs-title class_">SubStrategy</span>:
<span class="hljs-keyword">def</span> <span class="hljs-title function_">execute</span>(<span class="hljs-params">self, a: <span class="hljs-built_in">int</span>, b: <span class="hljs-built_in">int</span></span>):
<span class="hljs-keyword">return</span> a - b
<span class="hljs-keyword">class</span> <span class="hljs-title class_">MulStrategy</span>:
<span class="hljs-keyword">def</span> <span class="hljs-title function_">execute</span>(<span class="hljs-params">self, a: <span class="hljs-built_in">int</span>, b: <span class="hljs-built_in">int</span></span>):
<span class="hljs-keyword">return</span> a * b
<span class="hljs-comment"># 上下文类</span>
<span class="hljs-keyword">class</span> <span class="hljs-title class_">Calculator</span>:
<span class="hljs-keyword">def</span> <span class="hljs-title function_">__init__</span>(<span class="hljs-params">self, strategy: Strategy</span>):
self.strategy = strategy
<span class="hljs-keyword">def</span> <span class="hljs-title function_">set_strategy</span>(<span class="hljs-params">self, strategy: Strategy</span>):
<span class="hljs-string">"""允许在运行时切换策略"""</span>
self.strategy = strategy
<span class="hljs-keyword">def</span> <span class="hljs-title function_">calculate</span>(<span class="hljs-params">self, a: <span class="hljs-built_in">int</span>, b: <span class="hljs-built_in">int</span></span>) -> <span class="hljs-built_in">int</span>:
<span class="hljs-keyword">return</span> self.strategy.execute(a, b)
<span class="hljs-comment"># ===== 测试 =====</span>
<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">"__main__"</span>:
calc = Calculator(AddStrategy())
<span class="hljs-built_in">print</span>(<span class="hljs-string">"加法:"</span>, calc.calculate(<span class="hljs-number">5</span>, <span class="hljs-number">3</span>)) <span class="hljs-comment"># 输出 8</span>
calc.set_strategy(SubStrategy())
<span class="hljs-built_in">print</span>(<span class="hljs-string">"减法:"</span>, calc.calculate(<span class="hljs-number">5</span>, <span class="hljs-number">3</span>)) <span class="hljs-comment"># 输出 2</span>
calc.set_strategy(MulStrategy())
<span class="hljs-built_in">print</span>(<span class="hljs-string">"乘法:"</span>, calc.calculate(<span class="hljs-number">5</span>, <span class="hljs-number">3</span>)) <span class="hljs-comment"># 输出 15</span>
</code></div></pre>
<h3><a id="22_Pythonic_130"></a>2.2 函数式(Pythonic)版</h3>
<p>由于 Python 支持 函数作为一等对象,可以直接用函数替代策略类。</p>
<pre><div class="hljs"><code class="lang-python"><span class="hljs-comment"># -*- coding: utf-8 -*-</span>
<span class="hljs-keyword">def</span> <span class="hljs-title function_">add</span>(<span class="hljs-params">a, b</span>):
<span class="hljs-keyword">return</span> a + b
<span class="hljs-keyword">def</span> <span class="hljs-title function_">sub</span>(<span class="hljs-params">a, b</span>):
<span class="hljs-keyword">return</span> a - b
<span class="hljs-keyword">def</span> <span class="hljs-title function_">mul</span>(<span class="hljs-params">a, b</span>):
<span class="hljs-keyword">return</span> a * b
<span class="hljs-keyword">class</span> <span class="hljs-title class_">Calculator</span>:
<span class="hljs-keyword">def</span> <span class="hljs-title function_">__init__</span>(<span class="hljs-params">self, strategy</span>):
self.strategy = strategy
<span class="hljs-keyword">def</span> <span class="hljs-title function_">set_strategy</span>(<span class="hljs-params">self, strategy</span>):
self.strategy = strategy
<span class="hljs-keyword">def</span> <span class="hljs-title function_">calculate</span>(<span class="hljs-params">self, a, b</span>):
<span class="hljs-keyword">return</span> self.strategy(a, b)
calc = Calculator(add)
<span class="hljs-built_in">print</span>(<span class="hljs-string">"加法:"</span>, calc.calculate(<span class="hljs-number">5</span>, <span class="hljs-number">3</span>))
calc.set_strategy(sub)
<span class="hljs-built_in">print</span>(<span class="hljs-string">"减法:"</span>, calc.calculate(<span class="hljs-number">5</span>, <span class="hljs-number">3</span>))
calc.set_strategy(mul)
<span class="hljs-built_in">print</span>(<span class="hljs-string">"乘法:"</span>, calc.calculate(<span class="hljs-number">5</span>, <span class="hljs-number">3</span>))
</code></div></pre>
<p>优点:更简洁,符合 Python 风格。</p>
<p>缺点:没有类的结构,可能在大型系统中可读性稍差。</p>
<h2><a id="3__176"></a>3 支付策略模式</h2>
<pre><div class="hljs"><code class="lang-python"><span class="hljs-comment"># -*- coding: utf-8 -*-</span>
<span class="hljs-keyword">from</span> abc <span class="hljs-keyword">import</span> ABC, abstractmethod
<span class="hljs-comment"># 策略接口</span>
<span class="hljs-keyword">class</span> <span class="hljs-title class_">PaymentStrategy</span>(<span class="hljs-title class_ inherited__">ABC</span>):
<span class="hljs-meta"> @abstractmethod</span>
<span class="hljs-keyword">def</span> <span class="hljs-title function_">pay</span>(<span class="hljs-params">self, amount: <span class="hljs-built_in">float</span></span>) -> <span class="hljs-literal">None</span>:
<span class="hljs-keyword">pass</span>
<span class="hljs-comment"># 支付宝策略</span>
<span class="hljs-keyword">class</span> <span class="hljs-title class_">AliPayStrategy</span>(<span class="hljs-title class_ inherited__">PaymentStrategy</span>):
<span class="hljs-keyword">def</span> <span class="hljs-title function_">pay</span>(<span class="hljs-params">self, amount: <span class="hljs-built_in">float</span></span>) -> <span class="hljs-built_in">bool</span>:
<span class="hljs-built_in">print</span>(<span class="hljs-string">f"[支付宝] 支付 <span class="hljs-subst">{amount}</span> 元成功"</span>)
<span class="hljs-keyword">return</span> <span class="hljs-literal">True</span>
<span class="hljs-comment"># 微信策略</span>
<span class="hljs-keyword">class</span> <span class="hljs-title class_">WeChatPayStrategy</span>(<span class="hljs-title class_ inherited__">PaymentStrategy</span>):
<span class="hljs-keyword">def</span> <span class="hljs-title function_">pay</span>(<span class="hljs-params">self, amount: <span class="hljs-built_in">float</span></span>) -> <span class="hljs-built_in">bool</span>:
<span class="hljs-built_in">print</span>(<span class="hljs-string">f"[微信] 支付 <span class="hljs-subst">{amount}</span> 元成功"</span>)
<span class="hljs-keyword">return</span> <span class="hljs-literal">True</span>
<span class="hljs-comment"># 银行卡策略</span>
<span class="hljs-keyword">class</span> <span class="hljs-title class_">BankCardPayStrategy</span>(<span class="hljs-title class_ inherited__">PaymentStrategy</span>):
<span class="hljs-keyword">def</span> <span class="hljs-title function_">pay</span>(<span class="hljs-params">self, amount: <span class="hljs-built_in">float</span></span>) -> <span class="hljs-built_in">bool</span>:
<span class="hljs-built_in">print</span>(<span class="hljs-string">f"[银行卡] 支付 <span class="hljs-subst">{amount}</span> 元成功"</span>)
<span class="hljs-keyword">return</span> <span class="hljs-literal">True</span>
<span class="hljs-comment"># 支付上下文</span>
<span class="hljs-keyword">class</span> <span class="hljs-title class_">PaymentContext</span>:
<span class="hljs-keyword">def</span> <span class="hljs-title function_">__init__</span>(<span class="hljs-params">self, strategy: PaymentStrategy</span>):
self.strategy = strategy
<span class="hljs-keyword">def</span> <span class="hljs-title function_">execute_payment</span>(<span class="hljs-params">self, amount: <span class="hljs-built_in">float</span></span>) -> <span class="hljs-built_in">bool</span>:
<span class="hljs-keyword">return</span> self.strategy.pay(amount)
<span class="hljs-comment"># 策略映射表</span>
STRATEGY_MAP = {
<span class="hljs-string">"alipay"</span>: AliPayStrategy,
<span class="hljs-string">"wechat"</span>: WeChatPayStrategy,
<span class="hljs-string">"bank"</span>: BankCardPayStrategy,
}
<span class="hljs-comment"># 模拟后端接收请求</span>
<span class="hljs-keyword">def</span> <span class="hljs-title function_">handle_payment_request</span>(<span class="hljs-params">pay_type: <span class="hljs-built_in">str</span>, amount: <span class="hljs-built_in">float</span></span>):
strategy_class = STRATEGY_MAP.get(pay_type)
<span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> strategy_class:
<span class="hljs-keyword">raise</span> ValueError(<span class="hljs-string">f"不支持的支付方式: <span class="hljs-subst">{pay_type}</span>"</span>)
context = PaymentContext(strategy_class())
<span class="hljs-keyword">return</span> context.execute_payment(amount)
<span class="hljs-comment"># ===== 测试 =====</span>
<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">"__main__"</span>:
handle_payment_request(<span class="hljs-string">"alipay"</span>, <span class="hljs-number">100.0</span>)
handle_payment_request(<span class="hljs-string">"wechat"</span>, <span class="hljs-number">200.0</span>)
handle_payment_request(<span class="hljs-string">"bank"</span>, <span class="hljs-number">500.0</span>)
</code></div></pre>
<p><img src="https://static.couragesteak.com/article/349af7465270fbafd19a9a0937b6e36f.png" alt="image.png" /></p>
评论区