一些概念
# 策略模式
# 定义一系列算法,把它们一一封装起来,并且使他们可以相互替换,使得算法
# 可以独立于使用它的客户而变化。
# 模板方法
# 访问者模式
# 重构策略模式
# 代码:
# 经典的策略模式.py
# 使用函数实现策略模式.py
# 享元(flyweight)
# 是可共享的对象,可以同时在多个上下文中使用
# 命令模式 : 目的是解耦调用操作的对象(调用者)和提供实现的对象(接收者)
经典的策略模式:电商折扣.py
# 有1000或者以上积分 享受5%折扣
# 同一订单中单个商品数量达到20或者以上,享10%折扣
# 订单中的不同商品达到10个或以上,享7%折扣
# 一个订单只能享受一个折扣
"""上下文:
把计算委托给不同的算法可互换组件,本例为Order,它会根据不同的算法计算促销折扣
策略:
实现不同算法的共同接口,本例中Promotion这个抽象类扮演这个角色
具体策略:
策略的具体子类,fidelityPromo、BulkPromo\LargeOrderPromo是这里实现的三个具体策略"""
# 实现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,promition = None): # 客户\购物车\促销折扣
self.customer = customer
self.cart = cart
self.promotion = promition
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 = ''
return fmt.format(self.total(),self.due())
class Promotion: # 策略,抽象基类
@abstractmethod
def discount(self,order):
'''返回折扣金额(正值)'''
class FidelityPromo(Promotion): # 第一个具体策略
'''为积分1000及以上客户提供5%折扣'''
def discount(self,order):
return order.total() * .05 if order.customer.fidelity >= 1000 else 0
class BuckItemPromo(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):
distinct_items = {item.product for item in order.cart} # 去重
if len(distinct_items) >= 10:
return order.total()*.07
return 0
if __name__ == '__main__':
joe = Customer('John Doe',0)
ann = Customer('Ann Smith',1100)
cart = [LineItem('banana',4,.5),
LineItem('apple',10,1.5),
LineItem('watermellon',5,5.0)]
print(Order(joe, cart, FidelityPromo()))
print(Order(ann, cart, FidelityPromo()))
banana_cart = [LineItem('banana',30,.5),
LineItem('apple',10,1.5)]
print(Order(joe, banana_cart, BuckItemPromo()))
large_order = [LineItem(str(item_code),1,1.0)
for item_code in range(10)]
print(Order(joe, large_order, LargeOrderPromo()))
print(Order(joe, cart, LargeOrderPromo()))
使用函数实现策略模式.py
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,promition = None): # 客户\购物车\促销折扣
self.customer = customer
self.cart = cart
self.promotion = promition
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 = ''
return fmt.format(self.total(),self.due())
def fidelity_promo(order):
return order.total() * .05 if order.customer.fidelity >= 1000 else 0
def bulk_item_promo(order):
discount = 0
for item in order.cart:
if item.quantity >= 20:
discount += item.total() * .1
return discount
def large_order_promo(order):
distinct_items = {item.product for item in order.cart} # 去重
if len(distinct_items) >= 10:
return order.total() * .07
return 0
# 使用best_promo迭代一个函数列表,并找出折扣的最大值
# promos = [fidelity_promo,bulk_item_promo,large_order_promo]
# def best_promo(order):
# '''选择可用的最大折扣'''
# return max(promo(order) for promo in promos)
# 找出模块中的全部策略
# 内省模块的全局命名空间,动态构建promos列表 如果有新的策略函数
# 并且以_promo结尾则可以自动添加到策略函数列表中
promos = [globals()[name] for name in globals()
if name.endswith('_promo')
and name != 'best_promo'] # 过滤掉best_promo自身,防止无限递归
def best_promo(order):
'''选择可用的最大折扣'''
return max(promo(order) for promo in promos)
# 收集所有可用策略的另一种方法,是在一个单独的模块中保存所有的策略函数,把best_promo排除在外
# 内省单独的promotions模块,构建promos列表
# 使用这种方法不管怎么命名函数,都可以使用
promos = [func for name,func in not
inspect.getmembers(promotions,inspect.isfunction)] # getmembers用于获取对象的属性
"""
计算折扣只需要调用self.promotion
没有抽象基类
各个策略都是函数"""
if __name__ == '__main__':
joe = Customer('John Doe',0)
ann = Customer('Ann Smith',1100)
cart = [LineItem('banana',4,.5),
LineItem('apple',10,1.5),
LineItem('watermellon',5,5.0)]
# print(Order(joe, cart, fidelity_promo))
# print(Order(ann, cart, fidelity_promo))
banana_cart = [LineItem('banana',30,.5),
LineItem('apple',10,1.5)]
# print(Order(joe, banana_cart,bulk_item_promo))
large_order = [LineItem(str(item_code),1,1.0)
for item_code in range(10)]
# print(Order(joe, large_order, large_order_promo))
# print(Order(joe, cart, large_order_promo))
# best_promo测试
print(Order(joe, large_order, best_promo))
print(Order(joe, banana_cart, best_promo))
print(Order(ann, cart, best_promo))
# 找出模块中的全部策略
print(globals()) # 模块的全局命名空间 是一个字典