流畅的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

 

posted @ 2020-04-13 16:49  熊熊闯深林  阅读(235)  评论(0编辑  收藏  举报