记录博客 ZH-BLOG

Python 使用函数实现策略模式

时间:2018-08-07 16:38:28分类:python

假如一个网店制定了下述折扣规则:

有 1000 或以上积分的顾客, 每个订单享 5% 折扣;

同一订单中, 单个商品的数量达到 20 个或以上, 享 10% 折扣;

订单中的不同商品达到 10 个或以上, 享 7% 折扣;

一个订单一次只能享用一个折扣。

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.quantity*self.price

class Order:
    """账单"""
    def __init__(self,customer,cart,promotion=None):
        self.customer=customer
        self.cart=cart
        self.promotion=promotion

    def total(self):
        return sum(item.total for item in self.cart)

    def due(self):
        if self.promotion is None:
            return self.total()
        else:
            return self.total()-self.promotion.discount(self)

    def __repr__(self):
        fmt="";
        return fmt.format(self.total(),self.due())

    def __str__(self):
        return self.__repr__()


class Promotion(ABC):
    """策略模式"""
    @abstractmethod
    def discount(self,order):
        """根据折扣应减少金额"""

class FidelityPromo(Promotion):
    def discount(self,order):
        return order.total * 0.05 if order.customer.fidelity > 1000 else 0

class BulkItemPromo(Promotion):
    def discount(self, order):
        discount=0
        for item in order.cart:
            if item.quantity>=20:
                discount+= item.total * 0.1
        return discount

class LargeOrderPromo(Promotion):
    def discount(self, order):
        distinct_items={item.product for item in order.cart}
        return order.total * 0.07 if len(distinct_items) >= 10 else 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)]
print(Order(joe, cart, FidelityPromo()))
print(Order(ann, cart, FidelityPromo()))
banana_cart = [LineItem('banana', 30, .5),LineItem('apple', 10, 1.5)]
long_order = [LineItem(str(item_code), 50, 1.0) for item_code in range(10)]
print(Order(joe, long_order, LargeOrderPromo()))
print(Order(joe, long_order, BulkItemPromo()))
print(Order(joe, cart, LargeOrderPromo()))

使用函数实现策略模式

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):
        self.customer=customer
        self.cart=cart
        self.promotion=promotion

    def total(self):
        return sum(item.total() for item in cart)

    def due(self):
        if self.promotion is None:
            return self.total()
        else:
            return self.total()-self.promotion(self)

    def __str__(self):
        fmt=''
        return fmt.format(self.total(),self.due())

    def __repr__(self):
        return self.__str__()

def fidelity_promo(order):
    return order.total()*0.05 if order.customer.fidetity>1000 else 0

def bulkitem_promo(order):
    discount=0
    for item in order.cart:
        if item.quantity>=20:
            discount+=item.total()
    return discount

def large_order_promo(order):
    distinct_items={item.product for item in order}
    if len(distinct_items)>=10:
        return order.total()*0.07
    return 0

最佳策略

# 每新增策略需手动维护
promos=[fidelity_promo,bulkitem_promo,large_order_promo]

# 自动查找策略
promos=[val for name,val in globals().items() if name.endswith('_promo') and name!='best_promo']
def best_promo(order):
    return max(promo(order) for promo in promos)

使用 inspect 内省单独策略模块

将策略方法保存为 promotions.py 模块

promos = [func for name, func in inspect.getmembers(promotions, inspect.isfunction)]
def best_promo(order):
	"""选择可用的最佳折扣"""
	return max(promo(order) for promo in promos)