我们在开发过程中,经常会遇到同一个操作有多种实现方式的场景,比如计算折扣有常规折扣、季节性折扣、无折扣等。如果用if/elif来判断,代码会变得臃肿,而今天我们要说的策略模式,可以很好的解决这类问题。废话就不多说了,我们就以电商订单结算为例,实现一个简单的策略模式,需求很简单,订单结算时,可根据不同场景应用不同的折扣策略。首先,我们定义3种折扣策略(每个策略封装一个算法):class RegularDiscount: def apply(self,price): return price*0.95 # 常规折扣:95折class SeasonalDiscount: def apply(self,price): return price*0.8 #季节性折扣:8折class NoDiscount: def apply(self,price): return price # 无折扣:原价结算
这三个策略类都有一个相同apply方法,这就是策略的统一接口,也是它们能相互替换 的关键。接下来,定义上下文(订单类),它负责使用策略,不好含任何折扣逻辑,只委托策略完成计算:class Order: def __init__(self,product,price,discount_strategy): self.product = product # 商品名称 self.price = price # 商品原价 self.discount_strategy = discount_strategy #传入的折扣策略 def final_price(self): # 委托策略计算最终价格,自己不写折扣逻辑 return self.discount_strategy.apply(self.price) def summary(self): #打印订单详情 print(f"商品:{self.product}") print(f"原价:¥{self.price:.2f}") print(f"折扣价:¥{self.final_price():.2f}") print("-" * 30)
接下来我们创建几个订单,分别应用不同的折扣策略,看看效果:# 无折扣订单,键盘order1 = Order("Keyboard",120.00,NoDiscount())# 常规折扣订单:笔记本支架order2 = Order("Laptop Stand",45.00,RegularDiscount())#季节性折扣订单:USB-C集线器order3 = Order("USB-C Hub",35.00,SeansonDiscount())# 打印订单详情order1.summary()order2.summary()order3.summary()
商品:Keyboard原价:¥120.00折扣价:¥120.00------------------------------商品:Laptop Stand原价:¥45.00折扣价:¥42.75------------------------------商品:USB-C Hub原价:¥35.00折扣价:¥28.00------------------------------
可以看出Order类完全不用判断用哪种折扣,只需要调用策略的apply方法即可。如果以后要增加新的折扣, 只需要增加一个策略类,不用修改Order类的任何代码,这就是开闭原则。策略模式的优势之一是运行时切换策略,比如用户在购物过程中升级了会员,从无折扣切换到季节性折扣,不需要重启程序就能应用新策略。让我们以购物车为例子:class ShoppingCart: def __init__(self): self.items = [] # 购物车商品列表 self.discount_strategy = NoDiscount() # 默认无折扣 def add_item(self, name, price): # 添加商品到购物车 self.items.append({"name": name, "price": price}) def set_discount(self, strategy): # 动态切换折扣策略 self.discount_strategy = strategy print(f"折扣策略已更新为:{strategy.__class__.__name__}") def checkout(self): # 结算:计算所有商品的折后总价 print("\n--- 结算详情 ---") total = 0 for item in self.items: discounted_price = self.discount_strategy.apply(item["price"]) print(f"{item['name']}: ${discounted_price:.2f}") total += discounted_price print(f"结算总价:${total:.2f}\n")
重点是set_discount方法,我们可以在任何时候替换策略,接下来我们测试一下:# 初始化购物车cart = ShoppingCart()# 添加商品cart.add_item("Notebook", 15.00)cart.add_item("Desk Lamp", 40.00)cart.add_item("Monitor Riser", 25.00)# 第一次结算:普通用户(无折扣)cart.checkout()# 用户升级会员,切换为季节性折扣cart.set_discount(SeasonalDiscount())# 第二次结算:会员(8折)cart.checkout()
运行结果如下,清晰看到策略切换后的变化:
--- 结算详情 ---Notebook: $15.00Desk Lamp: $40.00Monitor Riser: $25.00结算总价:$80.00折扣策略已更新为:SeasonalDiscount--- 结算详情 ---Notebook: $12.00Desk Lamp: $32.00Monitor Riser: $20.00结算总价:$64.00
可以看到购物车本身逻辑没啥变化,只是切换了策略对象,就实现了不同的结算逻辑。也许有时在使用时,创建了一个策略类,但是又忘记了写apply方法,程序在运行时才会报错,这在排查时就比较麻烦,我们可以通过Python的抽象类来解决,强制所有的策略类必须实现apply方法,只要没实现,在程序实例化时就报错。from abc import ABC, abstractmethod# 定义抽象基类,规范策略接口class DiscountStrategy(ABC): @abstractmethod # 抽象方法,必须被子类实现 def apply(self, price: float) -> float: pass
# 常规折扣:继承抽象基类,实现apply方法class RegularDiscount(DiscountStrategy): def apply(self, price): return price * 0.95# 季节性折扣:继承抽象基类,实现apply方法class SeasonalDiscount(DiscountStrategy): def apply(self, price): return price * 0.80# 无折扣:继承抽象基类,实现apply方法class NoDiscount(DiscountStrategy): def apply(self, price): return price
当然,策略模式虽然好用,却不是所有场景都适用。当代码中有冗长的if/elif判断,并且判断逻辑是不同的算法或者行为时;需要在运行时动态切换行为;需要构建可扩展的系统;需要独立测试不同的算法时都可以适用这个模式。总之,策略模式的核心是用来解决问题,不是为了炫技而用。