一 策略模式
完成一件事通常有很多种方式,每一种方式都是一个策略。
策略模式(Strategy)定义了一组算法,将每个算法都封装起来,并且使它们之间可以互换。
经典的策略模式由三部分组成:
- Context:上下文环境类
- Stragety:策略基类
- ConcreteStragety:具体策略
Context用一个ConcreteStrategy来配置,维护一个对Strategy对象的引用;Strategy是策略类,用于定义所有支持算法的公共接口;ConcreteStrategy是具体策略类,封装了具体的算法或行为,继承于Strategy。
UML结构图如下:
二 策略模式的优缺点
优点
- 算法可以自由切换
- 避免使用多重条件判断(如果不用策略模式我们可能会使用多重条件语句,不利于维护)
- 扩展性良好,增加一个策略只需实现接口即可
缺点
- 策略类数量会增多,每个策略都是一个类,复用的可能性很小
- 所有的策略类都需要对外暴露
三 策略模式的示例
以超市优惠打折的三种策略为例,演示策略模式:
策略模式的三部分对应示例中的类如下:
- Context:Order类
- Stragety:Promotion类
- ConcreteStragety:BonusPointPromo、BulkPromo、LargeOrderPromo类
一些例子中用到的别的类:
class Item(object):
'''商品类 :商品种类、价格、数量,该商品的总价'''
def __init__(self,issue, price, quantity):
self.issue = issue
self.price = price
self.quantity = quantity
def itemtotal(self): #返回购买该商品的总价
return self.price * self.quantity
class Customer(object):
'''顾客类 :顾客名字、顾客的积分'''
def __init__(self, name ,bonuspoint):
self.name = name
self.bonuspoint = bonuspoint
Context:Order类
class Order(object):
'''订单类:顾客(主要用来查看其原始积分情况)、选择的优惠方案'''
def __init__(self, customer, promotion=None):
self.cart = []
self.customer = customer
self.promotion = promotion
def add_to_cart(self, *items): #加入购物车方法,传入不定长的Item类实例
for item in items:
self.cart.append(item)
def total(self): #计算订单的总价 为每种商品的总价和
total = 0
for item in self.cart:
total += item.itemtotal()
return total
def pay(self): #计算该订单需要实际出的钱
if not self.promotion: #如果没有优惠促销活动 那么折扣为0
discount = 0
else:
discount = self.promotion.discount(self) #有的话 计算折扣
payment = self.total() - discount #payment为最后需要付的钱
print(f'折扣策略{type(self.promotion).__name__}:原价{self.total()},折扣价: {payment}')
return payment
Stragety:Promotion类
from abc import ABC, abstractmethod
class Promotion(ABC):
@abstractmethod #定义抽象方法,子类必须重写discount方法
def discount(self, order):
pass
这里介绍一下上述用到的@abstractmethod的用法:
由于python 没有抽象类、接口的概念,所以要实现这种功能得abc.py 这个类库。
@abstractmethod:抽象方法,含abstractmethod方法的类不能实例化,继承了含abstractmethod方法的子类必须复写所有abstractmethod装饰的方法,未被装饰的可以不重写。
ConcreteStragety:BonusPointPromo、BulkPromo、LargeOrderPromo类
class BonusPointPromo(Promotion): #积分兑换的优惠策略,继承Promotion类
'''
积分满1000分,可以兑换20元现金券。该方法不能与别的优惠方案叠加使用
'''
def discount(self, order):
return 20 if order.customer.bonuspoint >=1000 else 0
class BulkPromo(Promotion): #大宗购买的优惠策略,继承Promotion类
'''
商品总数购买10件,总订单可打9折。该方法不能与别的优惠方案叠加使用
'''
def discount(self, order):
discount = 0
totalQuantity=0
for item in order.cart:
totalQuantity += item.quantity
if totalQuantity >= 10:
discount = order.total() * 0.1
return discount
class LargeOrderPromo(Promotion): #高额订单的优惠策略,继承Promotion类
'''
订单总金额大于等于500元,立减50元
'''
def discount(self, order):
discount = 0
if order.total() >= 500:
discount = 50
return discount
测试
if __name__ == '__main__':
ZhangSan = Customer('张三',1200) #有1200积分的张三去超市买东西
item1 = Item('卫生纸', 20, 10)
item2 = Item('玩偶', 100, 2)
item3 = Item('牛奶', 50, 4)
order = Order(ZhangSan, BonusPointPromo())
order.add_to_cart(item1, item2, item3)
pay1 = order.pay()
order = Order(ZhangSan, BulkPromo())
order.add_to_cart(item1, item2, item3)
pay2 = order.pay()
order = Order(ZhangSan, LargeOrderPromo())
order.add_to_cart(item1, item2, item3)
pay3 = order.pay()