1
“
策略
”
模式:
定义一系列算法,把它们一一封装起来,并且使它们可以相互替换。本模式使得算法可以独立于使用它的客户而变化。
电商领域有个功能明显可以使用
“
策略
”模式,即根据客户的属性或订单中的商品计算折扣。 假如一个网店制定了几条折扣规则,并且一个订单一次只能享用一个折扣。
程序如下:
注:在下面示例中,我们把折扣方法作为参数手动传入,实际中,应该是系统以某种方式去选择一种促销折扣策略,然后把它传给Order构造方法
from abc import ABC, abstractmethod
from collections import namedtuple
Customer = namedtuple('Customer', 'name fidelity')
class LineItem: # 商品类
def __init__(self, product, quantity, price):
self.product = product
self.quantity = quantity
self.price = price
def total(self):
return self.price * self.quantity
class Order: # 上下文
def __init__(self, customer, cart, promotion=None):
self.customer = customer
self.cart = list(cart) #商品类LineItem的实例的列表
self.promotion = promotion
def total(self):
if not hasattr(self, '__total'):
self.__total = sum(item.total() for item in self.cart)
return self.__total
def due(self):
if self.promotion is None:
discount = 0
else:
discount = self.promotion.discount(self)
return self.total() - discount
def __repr__(self):
fmt = '<Order total: {:.2f} due: {:.2f}>'
return fmt.format(self.total(), self.due())
class Promotion(ABC) : # 策略:抽象基类
@abstractmethod
def discount(self, order):
pass #返回折扣金额(正值)
class FidelityPromo(Promotion): # 第一个具体策略
"""为积分为1000或以上的顾客提供5%折扣"""
def discount(self, order):
return order.total() * .05 if order.customer.fidelity >= 1000 else 0
class BulkItemPromo(Promotion): # 第二个具体策略
"""单个商品为20个或以上时提供10%折扣"""
def discount(self, order):
discount = 0
for item in order.cart:
if item.quantity >= 20:
discount += item.total() * .1
return discount
class LargeOrderPromo(Promotion): # 第三个具体策略
"""订单中的不同商品达到10个或以上时提供7%折扣"""
def discount(self, order):
if len(order.cart) >= 10:
return order.total() * .07
return 0
上下文。把一些计算委托给实现不同算法的可互换组件,它提供服务。在这个电商示例中,上下文是 Order,它会根据不同的算法计算促销折扣。
策略。实现不同算法的组件共同的接口。在这个示例中,名为 Promotion 的抽象基类扮演这个角色。
具体策略。“策略”的具体子类。fidelityPromo、BulkPromo 和 LargeOrderPromo 是这里实现的三个具体策略。
对上面的程序运行测试:
if __name__ == '__main__':
apple = LineItem('apple',20,1.2)
iphone = LineItem('iphone',1,6000)
milk = LineItem('milk',10,3)
gyf = Customer('guoyunfei',1000)
zz = Customer('zzzz',20)
cart1 = [iphone]
cart2 = [apple,milk]
print(Order(gyf,cart1,promotion=FidelityPromo())) #积分为1000或以上的顾客提供5%折扣
print(Order(zz,cart2,promotion=BulkItemPromo())) #单个商品为20个或以上时提供10%折扣
运行结果:
<Order total: 6000.00 due: 5700.00>
<Order total: 54.00 due: 51.60>
2
进行重构,去掉抽象基类,把具体策略由类改为函数。
from abc import ABC, abstractmethod
from collections import namedtuple
Customer = namedtuple('Customer', 'name fidelity')
class LineItem: # 商品类
def __init__(self, product, quantity, price):
self.product = product
self.quantity = quantity
self.price = price
def total(self):
return self.price * self.quantity
class Order: # 上下文
def __init__(self, customer, cart, promotion=None):
self.customer = customer
self.cart = list(cart) #商品类LineItem的实例的列表
self.promotion = promotion
def total(self):
if not hasattr(self, '__total'):
self.__total = sum(item.total() for item in self.cart)
return self.__total
def due(self):
if self.promotion is None:
discount = 0
else:
discount = self.promotion(self)
return self.total() - discount
def __repr__(self):
fmt = '<Order total: {:.2f} due: {:.2f}>'
return fmt.format(self.total(), self.due())
# 第一个具体策略
"""为积分为1000或以上的顾客提供5%折扣"""
def discount1(order):
return order.total() * .05 if order.customer.fidelity >= 1000 else 0
# 第二个具体策略
"""单个商品为20个或以上时提供10%折扣"""
def discount2(order):
discount = 0
for item in order.cart:
if item.quantity >= 20:
discount += item.total() * .1
return discount
# 第三个具体策略
"""订单中的不同商品达到10个或以上时提供7%折扣"""
def discount3(order):
if len(order.cart) >= 10:
return order.total() * .07
return 0
if __name__ == '__main__':
apple = LineItem('apple',20,1.2)
iphone = LineItem('iphone',1,6000)
milk = LineItem('milk',10,3)
gyf = Customer('guoyunfei',1000)
zz = Customer('zzzz',20)
cart1 = [iphone]
cart2 = [apple,milk]
print(Order(gyf,cart1,promotion=discount1)) #积分为1000或以上的顾客提供5%折扣
print(Order(zz,cart2,promotion=discount2)) #单个商品为20个或以上时提供10%折扣
运行结果:
<Order total: 6000.00 due: 5700.00>
<Order total: 54.00 due: 51.60>
3
系统选择最佳促销折扣策略
#创建包含全部折扣函数的列表
discount_list = [discount1,discount2,discount3]
discount_list 代替折扣方法传入 Order。
对 Orde r中 due方法进行修改
def due(self):
if len(self.promotion) == 0:
discount = 0
else:
discount = max(d(self) for d in self.promotion)
return self.total() - discount
实际中,应该定义选择最佳策略的函数,然后把选择结果传给 Order 构造方法,如:
discount_list = [discount1,discount2,discount3]
def best_promo(order):
"""选择可用的最佳折扣 """
return max(d(order) for d in discount_list)
但这里如果要改的话,折扣方法也需要改,略。