# 《流畅的Python》读书笔记
# 第6章 使用一等函数实现设计模式
# 虽然设计模式与语言无关,但这并不意味着每一个模式都能在每一门语言中使用。
# 6.1 案例分析:重构“策略”模式
# 如果合理利用作为一等对象的函数,某些设计模式可以简化,“策略”模式就是其中一个很好的例子。
# 本节接下来的内容中将说明“策略”模式,并使用《设计模式:可复用面向对象软件的基础》一书中所述的“经典”结构实现它。
# 如果你熟悉这个经典模式,可以跳到 6.1.2 节,了解如何使用函数重构代码来有效减少代码行数。
# 6.1.1 经典的“策略”模式
# 示例 6-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)
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):
""""返回折扣金额(正值)"""
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):
distinct_items={item.product for item in order.cart}
if len(distinct_items) >= 10:
return order.total()* .07
return 0
# 示例 6-2 使用不同促销折扣的Order类示例
joe=Customer('John Doe',0) #joe 的积分是0
ann=Customer('Ann Smith',1100) #ann 的积分是1100。
cart=[ LineItem('banana',4,.5), #有三个商品的购物车。
LineItem('apple',10,1.5),
LineItem('watermellon',5,5.0) ]
print(Order(joe,cart,FidelityPromo())) #fidelityPromo 没给 joe 提供折扣。
#<Order total:42.00 due:42.00>
print(Order(ann,cart,FidelityPromo())) #ann 得到了5%折扣,因为她的积分超过 1000。
#<Order total:42.00 due:39.90>
banana_cart=[ LineItem('banana',30,.5), #banana_cart中有30把香蕉和10个苹果。
LineItem('apple',10,1.5) ]
print(Order(joe,banana_cart,BulkItemPromo())) #BulkItemPromo为joe购买的香蕉优惠了1.50美元。
#<Order total:30.00 due:28.50>
long_order=[ LineItem(str(item_code),1,1.0) #long_order中有10个不同的商品,每个商品的价格为1.00美元。
for item_code in range(10) ]
print(Order(joe,long_order,LargeOrderPromo())) #LargerOrderPromo 为 joe 的整个订单提供了 7% 折扣。
#<Order total:10.00 due:9.30>
print(Order(joe,cart,LargeOrderPromo()))
#<Order total:42.00 due:42.00>
# 6.1.2 使用函数实现“策略”模式
# 示例 6-3 Order 类和使用函数实现的折扣策略
# 示例 6-4 使用函数实现的促销折扣的 Order 类示例
# 6.1.3 选择最佳策略:简单的方式
# 示例 6-5 best_promo 函数计算所有折扣,并返回额度最大的
# 示例 6-6 best_promo 迭代一个函数列表,并找出折扣额度最大的
# 6.1.4 找出模块中的全部策略
# 示例 6-7 内省模块的全局命名空间,构建 promos 列表
# 示例 6-8 内省单独的 promotions 模块,构建 promos 列表
# 6.2 “命令”模式
# 示例 6-9 MacroCommand 的各个实例都在内部存储着命令列表
# 6.3 本章小结
# 6.4 延伸阅读