Python设计模式 第 16 章 策略模式(Strategy Pattern)

Python设计模式 第 16 章 策略模式(Strategy Pattern)在行为型模式中 策略模式是解决 多算法动态选择与封装 问题的核心模式 它就像 出行方式选择 用户 客户端 前往目的地时 可根据距离 成本 时间等因素 选择公交 地铁 打车 策略 等不同出行方式 每种方式 算法 的实现逻辑独立 且

欢迎大家来到IT世界,在知识的湖畔探索吧!

在行为型模式中,策略模式是解决 “多算法动态选择与封装” 问题的核心模式。它就像 “出行方式选择”—— 用户(客户端)前往目的地时,可根据距离、成本、时间等因素,选择公交、地铁、打车(策略)等不同出行方式,每种方式(算法)的实现逻辑独立,且可灵活切换。在软件开发中,当一个问题存在多种解决方案(如支付方式、排序算法、校验规则),且需根据场景动态选择方案,同时避免用大量if-else判断算法时,策略模式可通过 “算法封装” 将每种方案独立为策略类,实现 “算法与使用场景解耦、动态切换、易于扩展” 的目标。本章将从策略模式的核心概念出发,详细讲解其实现原理、Python 代码实现、实际应用场景及与其他模式的差异。

16.1 策略模式的定义与核心问题

16.1.1 定义

策略模式(Strategy Pattern)的官方定义为:定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化

简单来说,策略模式就像 “电商支付系统”—— 用户下单后,可选择支付宝、微信支付、银联(策略)等不同支付方式,每种支付方式的接口调用、参数格式、回调处理(算法逻辑)独立封装,系统(客户端)只需根据用户选择的支付方式,调用对应策略的支付方法,无需关心每种支付的具体实现。例如,数据排序场景中,可根据数据量选择冒泡排序(小规模)、快速排序(中大规模)、归并排序(需稳定排序),通过策略模式,排序逻辑与数据处理逻辑分离,可动态切换排序算法。

16.1.2 核心问题:硬编码算法判断的臃肿与扩展困境

在未使用策略模式时,处理多算法选择通常通过 “条件判断(if-else/switch)” 实现,面临以下问题:

  1. 代码臃肿且可读性差:所有算法逻辑集中在一个类或方法中,大量if-else判断算法类型(如if payment_type == “alipay”: … elif payment_type == “wechat”: …),代码冗长,难以维护;
  1. 算法扩展困难:新增算法(如新增 “Apple Pay” 支付方式)时,需在原有条件判断中新增分支,修改原有代码,违背开闭原则;
  1. 算法复用性低:同一算法(如 “微信支付”)需在多个场景使用时,需重复编写代码,无法独立复用;
  1. 算法切换不灵活:算法选择在编译时通过条件判断固定,无法在运行时根据动态条件(如用户会员等级、订单金额)切换算法(如会员用户优先使用 “银联支付” 享折扣);
  1. 责任边界模糊:算法逻辑与使用算法的业务逻辑(如订单创建、数据处理)混合在一起,修改算法需侵入业务代码,增加出错风险。

策略模式通过 “算法封装与委托” 解决上述问题:将每种算法封装为独立的 “策略类”,定义统一的策略接口,上下文对象(如订单支付管理器)持有当前策略对象的引用,业务逻辑中委托策略对象执行算法,算法切换时只需替换上下文的策略对象,实现 “算法与业务解耦、动态切换、易于扩展” 的目标。

16.1.3 生活中的策略模式案例

  • 出行方式选择:用户(客户端)→ 出行管理器(上下文)→ 公交 / 地铁 / 打车(策略),根据距离、时间选择不同出行方式,方式可灵活切换;
  • 电商支付方式:订单(客户端)→ 支付管理器(上下文)→ 支付宝 / 微信 / 银联(策略),用户选择支付方式,系统调用对应支付逻辑;
  • 折扣计算:购物车(客户端)→ 折扣管理器(上下文)→ 满减 / 折扣券 / 会员折扣(策略),根据订单金额、用户等级选择折扣方式;
  • 图像压缩:图片处理工具(客户端)→ 压缩管理器(上下文)→ JPEG/PNG/WebP(策略),根据图片类型、压缩质量需求选择压缩算法。

16.2 策略模式的核心角色

策略模式包含 4 个核心角色,通过 “策略封装与委托” 实现算法的动态选择与解耦,各角色职责明确且协作紧密:

角色名称

核心职责

实现方式(Python)

抽象策略(Strategy)

定义所有具体策略的公共接口,包含算法的核心方法(如 “支付”“排序”“折扣计算”),规范策略类的行为,确保策略可相互替换

抽象基类(abc.ABC),定义抽象算法方法,如execute()“calculate()”

具体策略(Concrete Strategy)

实现抽象策略的接口,包含具体的算法逻辑(如 “支付宝支付”“快速排序”),算法细节封装在类内部,与其他策略解耦

继承抽象策略类,重写抽象算法方法,实现具体业务逻辑(如alipay_pay()“quick_sort()”)

上下文(Context)

维护当前策略对象的引用,提供 “设置策略”(set_strategy())的方法,定义业务逻辑的公共接口(如 “处理支付”“执行排序”),并将算法执行委托给当前策略对象

普通类,包含current_strategy属性存储当前策略对象,实现业务方法(如process_payment()),方法内部调用current_strategy.execute()

客户端(Client)

创建具体策略对象和上下文对象,根据业务场景选择并设置初始策略(如根据用户选择的支付方式创建AlipayStrategy),通过上下文的业务接口使用算法,无需直接操作策略对象

初始化上下文和具体策略(如payment_context = PaymentContext(AlipayStrategy())),调用上下文的process_payment()等方法

角色间的协作流程

  1. 客户端创建 “上下文” 对象(如PaymentContext)和多个 “具体策略” 对象(如AlipayStrategy“WechatPayStrategy”);
  1. 客户端根据业务场景(如用户选择的支付方式),调用上下文的set_strategy()方法,设置当前策略对象;
  1. 客户端调用上下文的业务接口(如payment_context.process_payment(amount=100));
  1. 上下文将算法执行委托给当前策略对象(如self.current_strategy.pay(amount));
  1. 具体策略对象执行算法逻辑(如调用支付宝 API 发起支付),返回执行结果;
  1. 若需切换算法(如用户取消支付宝支付,选择微信支付),客户端调用上下文的set_strategy()方法替换策略对象,后续业务调用将使用新策略。

16.3 策略模式的 Python 实现(基础版)

以 “电商支付系统” 为例,实现包含 “支付宝”“微信支付”“银联支付” 3 种支付策略的上下文,客户端可根据用户选择动态切换支付方式,展示策略模式的核心逻辑。

16.3.1 步骤 1:定义抽象策略(Strategy)

创建 “支付策略” 抽象类,定义所有支付方式需实现的公共接口(pay()方法),规范支付策略的行为,确保不同支付方式可相互替换。

from abc import ABC, abstractmethod

欢迎大家来到IT世界,在知识的湖畔探索吧!

from typing import Dict, Optional

# 抽象策略:支付策略(PaymentStrategy)

class PaymentStrategy(ABC):

@abstractmethod

def pay(self, order_id: str, user_id: str, amount: float, extra_params: Optional[Dict] = None) -> Dict:

“””

抽象方法:执行支付操作

:param order_id: 订单ID

:param user_id: 用户ID

:param amount: 支付金额(元)

:param extra_params: 额外参数(如支付宝的openid、银联的卡号尾号)

:return: 支付结果(包含success、transaction_id、message等)

“””

pass

@abstractmethod

def get_strategy_name(self) -> str:

“””抽象方法:获取策略名称(用于日志、展示)”””

pass

16.3.2 步骤 2:实现具体策略(Concrete Strategy)

分别实现 “支付宝”“微信支付”“银联支付” 3 种具体支付策略,每个策略封装对应支付方式的接口调用逻辑(模拟第三方支付 API 调用),算法细节独立,不依赖其他策略。

策略 1:支付宝支付(AlipayStrategy)

欢迎大家来到IT世界,在知识的湖畔探索吧!# 具体策略1:支付宝支付(AlipayStrategy)

class AlipayStrategy(PaymentStrategy):

def pay(self, order_id: str, user_id: str, amount: float, extra_params: Optional[Dict] = None) -> Dict:

“””支付宝支付逻辑:模拟调用支付宝API”””

# 验证必要参数(支付宝需openid)

extra_params = extra_params or {}

openid = extra_params.get(“openid”)

if not openid:

return {

“success”: False,

“order_id”: order_id,

“strategy”: self.get_strategy_name(),

“message”: “支付宝支付缺少必要参数:openid”

}

# 模拟支付校验(金额>0、用户ID有效)

if amount <= 0:

return {

“success”: False,

“order_id”: order_id,

“strategy”: self.get_strategy_name(),

“message”: f”支付金额无效:{amount:.2f}元(需大于0)”

}

if not user_id.startswith(“user_”):

return {

“success”: False,

“order_id”: order_id,

“strategy”: self.get_strategy_name(),

“message”: f”用户ID格式无效:{user_id}(需以’user_’开头)”

}

# 模拟调用支付宝API成功,生成交易号

transaction_id = f”alipay_{order_id}_{user_id[-4:]}_{int(amount*100)}”

print(f”[支付宝支付] 订单{order_id},用户{user_id},金额{amount:.2f}元,openid={openid}”)

print(f”[支付宝支付] 调用API成功,交易号:{transaction_id}”)

return {

“success”: True,

“order_id”: order_id,

“user_id”: user_id,

“strategy”: self.get_strategy_name(),

“transaction_id”: transaction_id,

“amount”: amount,

“payment_time”: “2025-09-01 15:30:00”,

“message”: “支付宝支付成功”

}

def get_strategy_name(self) -> str:

return “支付宝支付”

策略 2:微信支付(WechatPayStrategy)

# 具体策略2:微信支付(WechatPayStrategy)

class WechatPayStrategy(PaymentStrategy):

def pay(self, order_id: str, user_id: str, amount: float, extra_params: Optional[Dict] = None) -> Dict:

“””微信支付逻辑:模拟调用微信支付API”””

extra_params = extra_params or {}

wechat_openid = extra_params.get(“wechat_openid”)

if not wechat_openid:

return {

“success”: False,

“order_id”: order_id,

“strategy”: self.get_strategy_name(),

“message”: “微信支付缺少必要参数:wechat_openid”

}

# 微信支付额外校验:金额需为整数分(此处简化为元,保留2位小数)

if round(amount * 100) != amount * 100:

return {

“success”: False,

“order_id”: order_id,

“strategy”: self.get_strategy_name(),

“message”: f”微信支付金额需为整数分:{amount:.2f}元(如10.00元、25.50元)”

}

# 模拟调用微信支付API成功

transaction_id = f”wechat_{order_id}_{wechat_openid[-6:]}_{int(amount*100)}”

print(f”[微信支付] 订单{order_id},用户{user_id},金额{amount:.2f}元,wechat_openid={wechat_openid}”)

print(f”[微信支付] 调用API成功,交易号:{transaction_id}”)

return {

“success”: True,

“order_id”: order_id,

“user_id”: user_id,

“strategy”: self.get_strategy_name(),

“transaction_id”: transaction_id,

“amount”: amount,

“payment_time”: “2025-09-01 15:30:05”,

“message”: “微信支付成功”

}

def get_strategy_name(self) -> str:

return “微信支付”

策略 3:银联支付(UnionPayStrategy)

欢迎大家来到IT世界,在知识的湖畔探索吧!# 具体策略3:银联支付(UnionPayStrategy)

class UnionPayStrategy(PaymentStrategy):

def pay(self, order_id: str, user_id: str, amount: float, extra_params: Optional[Dict] = None) -> Dict:

“””银联支付逻辑:模拟调用银联支付API”””

extra_params = extra_params or {}

card_last4 = extra_params.get(“card_last4”) # 银行卡尾号4位

if not card_last4 or len(card_last4) != 4 or not card_last4.isdigit():

return {

“success”: False,

“order_id”: order_id,

“strategy”: self.get_strategy_name(),

“message”: “银联支付缺少必要参数:card_last4(需为4位数字)”

}

# 银联支付额外校验:金额≥1元

if amount < 1.0:

return {

“success”: False,

“order_id”: order_id,

“strategy”: self.get_strategy_name(),

“message”: f”银联支付金额需≥1元:{amount:.2f}元”

}

# 模拟调用银联支付API成功

transaction_id = f”unionpay_{order_id}_{card_last4}_{int(amount*100)}”

print(f”[银联支付] 订单{order_id},用户{user_id},金额{amount:.2f}元,银行卡尾号:{card_last4}”)

print(f”[银联支付] 调用API成功,交易号:{transaction_id}”)

return {

“success”: True,

“order_id”: order_id,

“user_id”: user_id,

“strategy”: self.get_strategy_name(),

“transaction_id”: transaction_id,

“amount”: amount,

“payment_time”: “2025-09-01 15:30:10”,

“message”: “银联支付成功”

}

def get_strategy_name(self) -> str:

return “银联支付”

16.3.3 步骤 3:实现上下文(Context)

创建 “支付上下文” 类,维护当前支付策略对象的引用,提供 “设置支付策略” 的方法和 “处理支付” 的业务接口,将支付逻辑委托给当前策略对象执行,同时记录支付日志。

from datetime import datetime

from typing import Optional

# 上下文:支付管理器(PaymentContext)

class PaymentContext:

def __init__(self, initial_strategy: PaymentStrategy):

self.current_strategy: PaymentStrategy = None # 当前支付策略

self.payment_logs: list[Dict] = [] # 支付日志(记录所有支付尝试)

# 设置初始策略

self.set_strategy(initial_strategy)

def set_strategy(self, new_strategy: PaymentStrategy) -> None:

“””设置当前支付策略,支持动态切换”””

if not isinstance(new_strategy, PaymentStrategy):

raise TypeError(“支付策略必须是PaymentStrategy的子类”)

old_strategy_name =
self.current_strategy.get_strategy_name() if self.current_strategy else “无”

self.current_strategy = new_strategy

new_strategy_name = self.current_strategy.get_strategy_name()

print(f”\n[支付管理器] 支付策略切换:{old_strategy_name} → {new_strategy_name}”)

def get_payment_logs(self) -> list[Dict]:

“””获取支付日志(副本,避免外部修改)”””

return self.payment_logs.copy()

def process_payment(self, order_id: str, user_id: str, amount: float, extra_params: Optional[Dict] = None) -> Dict:

“””

业务接口:处理支付,委托当前策略执行

:param order_id: 订单ID

:param user_id: 用户ID

:param amount: 支付金额

:param extra_params: 支付方式额外参数

:return</doubaocanvas>

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://itzsg.com/140558.html

(0)
上一篇 25分钟前
下一篇 5分钟前

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

联系我们YX

mu99908888

在线咨询: 微信交谈

邮件:itzsgw@126.com

工作时间:时刻准备着!

关注微信