一 策略模式
完成一件事通常有很多种方式,每一种方式都是一个策略。
策略模式(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的用法:
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() 
结果如下:











