案例分析:重构“策略”模式
如果合理利用作为一等对象的函数,某些设计模式可以简化,“策略”模式就是其中一个很好的例子。
经典的“策略”模式
使用“策略”设计模式处理订单折扣的 UML 类图
电商领域有个功能明显可以使用“策略”模式,即根据客户的属性或订单中的商品计算折扣。
假如一个网店制定了下述折扣规则:
- 有1000或者以上积分的客户,每个订单享5%折扣
- 同一个订单中,单个商品的数量达到20个或以上,享10%折扣
- 订单中的不同商品达到10个或以上的,享7%的折扣
简单起见,我们假定一个订单一次只能享用一个折扣。
上下文
把一些计算委托给实现不同算法的可交互组件,它提供服务。在这个点上实例中,上下文是Order,它会根据不同的算法计算促销折扣
策略
实现不同算法的组件共同的接口,在这个实例中,名为Promotion的抽象类扮演这个角色
具体策略
“策略”的具体子类。fidelityPromo、BulkPromo和LargeOrderPromo是这里实现的三个具体策略
1 from abc import ABC, abstractmethod 2 from collections import namedtuple 3 4 5 Customer = namedtuple('Customer', 'name fidelity') 6 7 8 class LineItem: 9 10 def __init__(self, product, quantity, price): 11 self.product = product 12 self.quantity = quantity 13 self.price = price 14 15 def total(self): 16 return self.price * self.quantity 17 18 19 class Order: 20 21 def __init__(self, customer, cart, promotion=None): 22 self.customer = customer 23 self.cart = list(cart) 24 self.promotion = promotion 25 26 def total(self): 27 if not hasattr(self, '__total'): 28 self.__total = sum(item.total() for item in self.cart) 29 return self.__total 30 31 def due(self): 32 if self.promotion is None: 33 discount = 0 34 else: 35 discount = self.promotion.discount(self) 36 return self.total() - discount 37 38 def __repr__(self): 39 fmt = '<Order total: {:.2f} due: {:.2f}>' 40 return fmt.format(self.total(), self.due()) 41 42 class Promotion(ABC): 43 44 @abstractmethod 45 def discount(self, order): 46 """返回折扣金额(正值)""" 47 48 49 class FidelityPromo(Promotion):# 第一个具体策略 50 """为积分为1000货以上的顾客提供5%的折扣""" 51 52 def discount(self, order): 53 return order.total() * .05 if order.customer.fidelity >= 1000 else 0 54 55 56 class BulkItemPromo(Promotion): # 第二个具体策略 57 """单个商品为20个或以上时提供10%折扣""" 58 59 def discount(self, order): 60 discount = 0 61 for item in order.cart: 62 if item.quantity >= 20: 63 discount += item.total() * .1 64 return discount 65 66 67 class LargeOrderPromo(Promotion): # 第三个具体策略 68 """订单中的不同商品达到10个或以上时提供7%折扣""" 69 70 def discount(self, order): 71 distinct_items = {item.product for item in order.cart} 72 if len(distinct_items) >= 10: 73 return order.total() * .07 74 return 0 75 76 77 #两个顾客:joe 的积分是 0,ann 的积分是 1100 78 joe = Customer('John Doe', 0) 79 ann = Customer('Ann Smith', 1100) 80 81 #有三个商品的购物车 82 cart = [LineItem('banana', 4, .5), 83 LineItem('apple', 10, 1.5), 84 LineItem('watermellon', 5, 5.0) 85 ] 86 87 #fidelityPromo 没给 joe 提供折扣 88 print('joe 0积分:', Order(joe, cart, FidelityPromo())) 89 #ann 得到了 5% 折扣,因为她的积分超过 1000 90 print('ann 1000积分:', Order(ann, cart, FidelityPromo())) 91 92 #banana_cart 中有 30 把香蕉和 10 个苹果 93 banana_cart = [LineItem('banana', 30, .5), 94 LineItem('apple', 10, 1.5) 95 ] 96 97 #BulkItemPromo 为 joe 购买的香蕉优惠了 1.50 美元 98 print('joe banana cart:', Order(joe, banana_cart, BulkItemPromo())) 99 100 #long_order 中有 10 个不同的商品,每个商品的价格为 1.00 美元 101 long_order = [LineItem(str(item_code), 1, 1.0) for item_code in range(10)] 102 103 #LargerOrderPromo 为 joe 的整个订单提供了 7% 折扣 104 print('joe 10个不同产品:', Order(joe, long_order, LargeOrderPromo())) 105 print(Order(joe, cart, LargeOrderPromo()))
以上代码执行的结果为:
joe 0积分: <Order total: 42.00 due: 42.00> ann 1000积分: <Order total: 42.00 due: 39.90> joe banana cart: <Order total: 30.00 due: 28.50> joe 10个不同产品: <Order total: 10.00 due: 9.30> <Order total: 42.00 due: 42.00>
使用函数实现“策略”模式