标签:image 功能 相关 算法 苹果 col 分享图片 asa 有一个
经典的“策略”模式
电商领域有个功能明显可以使用“策略”模式,即根据客户的属性或订单
中的商品计算折扣。
假如一个网店制定了下述折扣规则。
简单起见,我们假定一个订单一次只能享用一个折扣。
上下文
把一些计算委托给实现不同算法的可互换组件,它提供服务。在这个电商示例中,上下文是 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, 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
在某个实现了上述规则的模块中演示和验证相关操作。
>>> 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)] >>> Order(joe, cart, FidelityPromo()) ? <Order total: 42.00 due: 42.00> >>> Order(ann, cart, FidelityPromo()) ? <Order total: 42.00 due: 39.90> >>> banana_cart = [LineItem(‘banana‘, 30, .5), ? ... LineItem(‘apple‘, 10, 1.5)] >>> Order(joe, banana_cart, BulkItemPromo()) ? <Order total: 30.00 due: 28.50> >>> long_order = [LineItem(str(item_code), 1, 1.0) ? ... for item_code in range(10)] >>> Order(joe, long_order, LargeOrderPromo()) ? <Order total: 10.00 due: 9.30> >>> Order(joe, cart, LargeOrderPromo()) <Order total: 42.00 due: 42.00>
? 两个顾客:joe 的积分是 0,ann 的积分是 1100。
? 有三个商品的购物车。
? fidelityPromo 没给 joe 提供折扣。
? ann 得到了 5% 折扣,因为她的积分超过 1000。
? banana_cart 中有 30 把香蕉和 10 个苹果。
? BulkItemPromo 为 joe 购买的香蕉优惠了 1.50 美元。
? long_order 中有 10 个不同的商品,每个商品的价格为 1.00 美元。
? LargerOrderPromo 为 joe 的整个订单提供了 7% 折扣。
示例 6-1 完全可用,但是利用 Python 中作为对象的函数,可以使用更少
的代码实现相同的功能。详情参见下一节。
“命令”设计模式也可以通过把函数作为参数传递而简化。这一模式对类的编排如图
各个命令可以有不同的接收者(实现操作的对象)。对
PasteCommand 来说,接收者是 Document。对 OpenCommand 来说,接收者是应用程序
“命令”模式的目的是解耦调用操作的对象(调用者)和提供实现的对象(接收者)。在《设计模式:可复用面向对象软件的基础》所举的示例中,调用者是图形应用程序中的菜单项,而接收者是被编辑的文档或应用程序自身。
这个模式的做法是,在二者之间放一个 Command 对象,让它实现只有一个方法(execute)的接口,调用接收者中的方法执行所需的操作。这样,调用者无需了解接收者的接口,而且不同的接收者可以适应不同的 Command 子类。调用者有一个具体的命令,通过调用 execute 方法执行。注意,图中的 MacroCommand 可能保存一系列命令,它的execute() 方法会在各个命令上调用相同的方法。
我们可以不为调用者提供一个 Command 实例,而是给它一个函数。此时,调用者不用调用 command.execute(),直接调用 command() 即可。MacroCommand 可以实现成定义了 __call__ 方法的类。这
样,MacroCommand 的实例就是可调用对象,各自维护着一个函数列表,供以后调用,
class MacroCommand: """一个执行一组命令的命令""" def __init__(self, commands): self.commands = list(commands) # ? def __call__(self): for command in self.commands: # ? command()
? 使用 commands 参数构建一个列表,这样能确保参数是可迭代对象,还能在各个 MacroCommand 实例中保存各个命令引用的副本。
? 调用 MacroCommand 实例时,self.commands 中的各个命令依序执行。
使用一等函数对“命令”模式的重新审视到此结束。站在一定高度上看,这里采用的方式与“策略”模式所用的类似:把实现单方法接口的类的实
例替换成可调用对象。毕竟,每个 Python 可调用对象都实现了单方法接口,这个方法就是 __call__。
标签:image 功能 相关 算法 苹果 col 分享图片 asa 有一个
原文地址:https://www.cnblogs.com/chenxuming/p/9709946.html