流畅的python学习:策略模式
经典策略模式
# -*- coding:utf-8 -*- from abc import ABC, abstractmethod from collections import namedtuple Customer = namedtuple('Customer', 'name fidellty') class LineItem: def __init__(self, product, quantity, price): self.product = product self.quantity = quantity self.price = price def total(self): return self.quantity * self.price class Order: def __init__(self, customer, cart, promotion=None): self.customer = customer # 传入的cart可能是生成器,转换成列表 self.cart = list(cart) self.promotion = promotion def total(self): if not hasattr(self, '__total'): # 如果类没有__total的属性,曾进行操作 # 已经存在不重新计算?订单修改重新计算? # cart更新重新计算,不修改 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 max_due(self): # promitions = [LargeOrderPromotion, BulkItemPromotion, FidelityPromotion] # max_discount = {promition.__name__:promition().discount(self) for promition in promitions} # print(max_discount) # print(max_discount.get()) # # m = max(max_discount,key=max_discount.get()) # print(m) # print('max discount=' + str(max_discount)) # return 'max 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 FidelityPromotion(Promotion): '''为1000积分以上的顾客提供5%的折扣''' def discount(self, order): return order.total() * 0.5 if order.customer.fidellty >= 1000 else order.total() class BulkItemPromotion(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 LargeOrderPromotion(Promotion): '''订单中不同商品到达10个或以上时提供折扣7%''' a=3 def __init__(self): pass def discount(self, order): # 集合去重复 distinct_item = {item.product for item in order.cart} # print(distinct_item) # print(self.a) # print(order.cart) if len(distinct_item) >= 10: return order.total() * .07 return 0 # print(__name__ == "__main__") # print() if __name__ == "__main__": joe = Customer('John Doe', 1000) long_order = [LineItem(str(item), 20, 1.0) for item in range(10)] # max_promotion = max(discount() for discount in promition) o = Order(joe,long_order,LargeOrderPromotion()) # print(o.max_due()) o = Order(joe,long_order,LargeOrderPromotion()) print(o)
抽象一个类策略,用户传入到订单order中,对订单进行相应的优惠策略。
每个类中都只有一个方法,可以考虑使用函数当做一等函数传入,进行代码简化
一等函数改写经典策略模式
# -*- coding:utf-8 -*- from collections import namedtuple Customer = namedtuple('Customer', 'name fidellty') class LineItem: def __init__(self, product, quantity, price): self.product = product self.quantity = quantity self.price = price def total(self): return self.quantity * self.price class Order: def __init__(self, customer, cart, promotion=None): self.customer = customer # 传入的cart可能是生成器,转换成列表 self.cart = list(cart) self.promotion = promotion def total(self): if not hasattr(self, '__total'): # 如果类没有__total的属性,曾进行操作 # 已经存在不重新计算?订单修改重新计算? # cart更新重新计算,不修改 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()) def FidelityPromotion(order): return order.total() * 0.5 if order.customer.fidellty>=1000 else 0 def BulkItemPromotion(order): discount = 0 for item in order.cart: if item.quantity >= 20: discount += item.total() * 0.1 return discount def LargeOrderPromotion(order): distinct_item = {procduct for procduct in order.cart} if len(distinct_item) >=10: return order.total() * 0.07 return order.total() if __name__ == "__main__": # max_promotion = max(discount() for discount in promition) joe = Customer('张三',50) long_order = [LineItem(str(item), 20, 1.0) for item in range(10)] o = Order(joe,long_order,LargeOrderPromotion) print(o) # print(o.max_due()) o = Order(joe,long_order,BulkItemPromotion) print(o) o = Order(joe,long_order,FidelityPromotion) print(o)
把函数当作对象传入到类内部,简化了代码,同时也简化了操作难度。可以更加便利的实现求最优策略
def get_max_cheap(order): promotions = [FidelityPromotion, BulkItemPromotion, LargeOrderPromotion] max_discount = {promotion.__name__:Order(joe, long_order, promotion).due() for promotion in promotions} print(type(max_discount),max_discount) key_max = max(max_discount.keys(), key = (lambda k: max_discount[k])) key_min = min(max_discount.keys(), key = (lambda k: max_discount[k])) print("max promotion is %s,max values is %s" % (key_max, max_discount[key_max])) print("min promotion is %s,min values is %s" % (key_min, max_discount[key_min]))
这样子操作有一个缺点,就是行数整个运行依托于列表promotions,如果有新的折扣函数,无法及时更新promotions列表。
需要考虑,新增一个函数注册功能,把发现的函数新增到列表中
promotions = [] def regest(f): def in_re(*args,**kwargs): promotions.append(f) res = f(*args,**kwargs) return res return in_re
编写装饰器,需要的参与最优计算得策略,都加上装饰器,使用装饰器完成注册,添加到列表promotions,有策略添加和删除,只需要加上相应的装饰器,即可完成注册
此装饰器,可以简写成
promotions = [] def regest(f): promotions.append(f) return f
在变体,添加参数,可以使装饰器可以支持方法注册及注销
promotions = [] def regest(res = True): def is_regest(f): if res: promotions.append(f) elif f in promotions: promotions.remove(f) return f return is_regest