经典的策略模式
示例10-1 实现Order
类,支持插入式折扣策略
from abc import ABC, abstractmethod
from collections.abc import Sequence
from decimal import Decimal
from typing import NamedTuple, Optional
class Customer(NamedTuple):
name: str
fidelity: int
class LineItem(NamedTuple):
product: str
quantity: int
price: Decimal
def total(self) -> Decimal:
return self.price * self.quantity
class Order(NamedTuple): # 上下文
customer: Customer
cart: Sequence[LineItem]
promotion: Optional['Promotion'] = None
def total(self) -> Decimal:
totals = (item.total() for item in self.cart)
return sum(totals, start=Decimal(0))
def due(self) -> Decimal:
if self.promotion is None:
discount = Decimal(0)
else:
discount = self.promotion.discount(self)
return self.total() - discount
def __repr__(self):
return f'<Order total: {self.total():.2f} due: {self.due():.2f}>'
class Promotion(ABC): # 策略:抽象基类
@abstractmethod
def discount(self, order: Order) -> Decimal:
"""返回折扣金额(正值)"""
class FidelityPromo(Promotion): # 第一个具体策略
"""为积分为1000或以上的顾客提供5%折扣"""
def discount(self, order: Order) -> Decimal:
rate = Decimal('0.05')
if order.customer.fidelity >= 1000:
return order.total() * rate
return Decimal(0)
class BulkItemPromo(Promotion): # 第二个具体策略
"""单个商品的数量为20个或以上时提供10%折扣"""
def discount(self, order: Order) -> Decimal:
discount = Decimal(0)
for item in order.cart:
if item.quantity >= 20:
discount += item.total() * Decimal('0.1')
return discount
class LargeOrderPromo(Promotion): # 第三个具体策略
"""订单中不同商品的数量达到10个或以上时提供7%折扣"""
def discount(self, order: Order) -> Decimal:
distinct_items = {item.product for item in order.cart}
if len(distinct_items) >= 10:
return order.total() * Decimal('0.07')
return Decimal(0)
注意,示例 10-1 把 Promotion
定义为了抽象基类,这么做是为了使用
@abstractmethod
装饰器,明确表明所用的模式。
示例10-2 使用不同促销折扣的Order类示例
示例 10-1 完全可用,但是在 Python 中,如果把函数当作对象使用,则
实现同样的功能所需的代码更少。
使用函数实现策略模式
在示例 10-1 中,每个具体策略都是一个类,而且都只定义了一个方
法,即 discount
。此外,策略实例没有状态(没有实例属性)。你可
能会说,它们看起来像是普通函数——的确如此。示例 10-3 是对示例
10-1 的重构,把具体策略换成了简单的函数,而且去掉了抽象类
Promo
。Order
类也要修改,但改动不大。
示例10-3 Order
类和使用函数实现的折扣策略
from collections.abc import Sequence
from dataclasses import dataclass
from decimal import Decimal
from typing import Optional, Callable, NamedTuple
class Customer(NamedTuple):
name: str
fidelity: int
class LineItem(NamedTuple):
product: str
quantity: int
price: Decimal
def total(self):
return self.price * self.quantity
@dataclass(frozen=True)
class Order: # 上下文
customer: Customer
cart: Sequence[LineItem]
promotion: Optional[Callable[['Order'], Decimal]] = None # 1
def total(self) -> Decimal:
totals = (item.total() for item in self.cart)
return sum(totals, start=Decimal(0))
def due(self) -> Decimal:
if self.promotion is None:
discount = Decimal(0)
else:
discount = self.promotion(self) # 2
return self.total() - discount
def __repr__(self):
return f'<Order total: {self.total():.2f} due: {self.due():.2f}>' # 3
def fidelity_promo(order: Order) -> Decimal: # 4
"""为积分为1000或以上的顾客提供5%折扣"""
if order.customer.fidelity >= 1000:
return order.total() * Decimal('0.05')
return Decimal(0)
def bulk_item_promo(order: Order) -> Decimal:
"""单个商品的数量为20个或以上时提供10%折扣"""
discount = Decimal(0)
for item in order.cart:
if item.quantity >= 20:
discount += item.total() * Decimal('0.1')
return discount
def large_order_promo(order: Order) -> Decimal:
"""订单中不同商品的数量达到10个或以上时提供7%折扣"""
distinct_items = {item.product for item in order.cart}
if len(distinct_items) >= 10:
return order.total() * Decimal('0.07')
return Decimal(0)
❶ 这个类型提示的意思是,promotion
既可以是 None
,也可以是接收
一个 Order
参数并返回一个 Decimal
值的可调用对象。
❷ 调用可调用对象 self.promotion
,传入 self
,计算折扣。
❸ 没有抽象类。
❹ 各个策略都是函数。
标签:10,promo,python,Decimal,第十章,order,--,Order,self From: https://www.cnblogs.com/bonne-chance/p/18241671