使用一等函数实现设计模式。
6.1案例分析:重构策略模式。
根据策略模式定制一个网店的折扣模式:
1、有1000或以上积分的顾客,每个订单享5%的折扣
2、同一个订单中,单个商品的数量达到20个或以上,享受10%折扣
3、订单中的不同商品达到10个或以上,享7%折扣。
一个订单只能享受一个折扣。
根据书中的策略模式,我按照书中的方式写了代码,其实order属于上下文,它接收了所有的数据,进行统一汇总输出。
order
from collections import namedtuple from lineitem import LineItem from promo_all import * Customer = namedtuple('Customer', 'name fidelity') class Order: # 上下文 def __init__(self, customer, cart, promotion=None): '''参数1用户模型,参数2购物车,参数3优惠方案''' 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 == None: discount = 0 else: discount = self.promotion.discount(self) return self.total() - discount def __repr__(self): '''格式化输出实际价格以及优惠后的价格''' fmt = '' return fmt.format(self.total(), self.due()) if __name__ == '__main__': joe = Customer('John', 0) ann = Customer('Ann Smith', 11000) cart = [ LineItem('banana', 4, .5), LineItem('apple', 10, 1.5), LineItem('watermellon', 5, 5) ] joe_o = Order(joe, cart, FidelityPromo()) print(joe_o) ann_o = Order(ann, cart, FidelityPromo()) print(ann_o)
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
from abc import ABC from abc import abstractmethod class Promotion(ABC): @abstractmethod def discount(self, order): '''返回折扣的金额'''
from promotion import Promotion class FidelityPromo(Promotion): '''为积分为1000或以上的顾客的5%折扣''' def discount(self, order): '''order就是上下文对接本身''' return order.total() * .05 if order.customer.fidelity >= 1000 else 0 class BulkItemPromo(Promotion): '''单个商品为20个或以上''' def discount(self, order): discount = 0 for item in order.carr(): if item.quantity >= 20: discount += item.total * .1 return discount class LargeOrderPromo(Promotion): '''订单中的不同商品达到10个或以上提供的优惠''' def discount(self, order): distinct_items = {item.product for item in order.cart} if len(distinct_items) >= 10: return order.total() * .7 return 0
代码中用到了抽象基类。
从代码中发现,传入的优惠是一个实例,里面也只有简单的计算功能,完全可以改成函数。
from collections import namedtuple from lineitem import LineItem from promo_all import * from promo_all_func import * Customer = namedtuple('Customer', 'name fidelity') class Order: # 上下文 def __init__(self, customer, cart, promotion=None): '''参数1用户模型,参数2购物车,参数3优惠方案''' 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 == None: discount = 0 else: # discount = self.promotion.discount(self) discount = self.promotion(self) return self.total() - discount def __repr__(self): '''格式化输出实际价格以及优惠后的价格''' fmt = '' return fmt.format(self.total(), self.due()) if __name__ == '__main__': joe = Customer('John', 0) ann = Customer('Ann Smith', 11000) cart = [ LineItem('banana', 4, .5), LineItem('apple', 10, 1.5), LineItem('watermellon', 5, 5) ] joe_o = Order(joe, cart, fidelity_promo) print(joe_o) ann_o = Order(ann, cart, fidelity_promo) print(ann_o)
def fidelity_promo(order): '''order就是上下文对接本身''' return order.total() * .05 if order.customer.fidelity >= 1000 else 0 def bulkitem_promo(order): discount = 0 for item in order.carr(): if item.quantity >= 20: discount += item.total * .1 return discount def largeorder_promo(order): distinct_items = {item.product for item in order.cart} if len(distinct_items) >= 10: return order.total() * .7 return 0
改函数的话,order里面就在优惠的执行上面改了一下,不需要调用方法,直接()调用函数。
因为有三个不同的优惠方案,选一个合适的方法。当然也可以通过函数实现。(书中介绍了三种导入优惠函数的方法。)
第一种也是我这样的人想到的一种:
promos = [fidelity_promo, bulkitem_promo, largeorder_promo] def best_promo(order): '''选择最佳折扣方案''' return max(promo(order) for promo in promos)
直接导入三个函数,循环执行函数,然后挑出优惠最大的。这个唯一的不方便就是每次都要自己手动添加函数。
第二种用过globals函数执行,取出所有的赋值对象。
promos = [globals()[name] for name in globals() if name.endswith('_promo') and name != 'best_promo' ] def best_promo(order): '''选择最佳折扣方案''' return max(promo(order) for promo in promos)
然后根据条件选择出自己所要的函数,唯一的缺点,函数的名字需要统一名称。
第三种,通过inspect的使用,导入整个模块,判断里面的模块是否是函数,和姓名等信息,输出到列表。
import promo_all_func import inspect # 导入模块通过inspect.isfunction来获取木块内的方法于属性,通过条件判断, # 输出需要的函数,最方便的。 promos = [func for name,func in inspect.getmembers (promo_all_func, inspect.isfunction) if name != 'best_promo'] print(best_promo(ann_o))
三种不同的方法,明显最后一种最方便了,如果有后续的优惠方案,直接添加进去就可以了。
over,这个章节主要讲述了把函数当做对象传递比传递实例方便的操作。