linux 短视频开发 jetbrains WebService 5G jsf knockoutjs vcpkg datagrid react视频教程 nginx视频教程 小程序demo源码 pytorch安装教程 mysql升序 mysql合并结果集 coreldraw入门学习 mysql数据库 mysql建表 mysql 选择数据库 mysql更新 python环境设置 python中items python类与对象 python查找指定字符 javaswitch java日期 java覆盖 java方法 java入门代码 java字符串比较 java输出 java配置文件 linuxgrep linux目录 linux系统命令大全 pascal教程 linux格式化命令 高效能人士的七个习惯下载 hadoop权威指南 数据库系统概论第五版
当前位置: 首页 > 学习教程  > python

说说如何使用 Python 函数实现策略模式

2021/2/7 11:09:35 文章标签: 测试文章如有侵权请发送至邮箱809451989@qq.com投诉后文章立即删除

假设某电商平台网店制定了下述折扣规则: A. 有 1000 或以上积分的顾客,每个订单享 5% 折扣; B. 同一订单中,单个商品的数量达到 20 个或以上,享 10% 折扣; C. 订单中的不同商品达到 10 个或以上&#xff0c…

假设某电商平台网店制定了下述折扣规则:
A. 有 1000 或以上积分的顾客,每个订单享 5% 折扣;
B. 同一订单中,单个商品的数量达到 20 个或以上,享 10% 折扣;
C. 订单中的不同商品达到 10 个或以上,享 7% 折扣。

为了方便说明问题,我们假定一个订单一次只能享用一个折扣。

这个功能很明显应该采用策略设计模式,我们先画出类图:

类图属性栏中的 - 表示 private1

虚线三角箭头表示类实现关系,实现类指向接口;虚线箭头表示类依赖关系,箭头指向依赖类。

1 传统实现方式

1.1 定义

代码实现如下:

from abc import ABC, abstractmethod
from collections import namedtuple

Customer = namedtuple('Customer', 'name fidelity')


class LineItem:

    def __init__(self, product, quantity, price):
        self.price = price
        self.quantity = quantity
        self.product = product

    def total(self):
        return self.price * self.quantity


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

    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
  1. 在 Python 3.4 + 中,声明抽象基类最简单的方式是子类化 abc.ABC。ABC 模块提供了一个元类 ABCMeta,可以用来定义抽象类,另外还提供一个工具类 ABC,通过它可以按照继承的方式来定义抽象基类2。其中的 @abc.abstractmethod 是用于声明抽象方法的装饰器,类似于 Java 中的注解。

  2. 我们利用 namedtuple 创建了一个具名元组。Python 引入了 collections.namedtuple 这个工厂函数,用来构造一个带字段名的元组3

  3. 然后定义了三个策略类,它们都继承自 Promotion,并实现了各自的 discount() 方法。

1.2 使用

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)]

result = Order(joe, cart, FidelityPromo())
logging.info('result -> %s', result)

result = Order(ann, cart, FidelityPromo())
logging.info('result -> %s', result)

banana_cart = [LineItem('banana', 30, 5), LineItem('apple', 10, 1.5)]
result = Order(joe, banana_cart, BulkItemPromo())
logging.info('result -> %s', result)

long_order = [LineItem(str(item_code), 1, 1.0) for item_code in range(10)]
result = Order(joe, long_order, LargeOrderPromo())
logging.info('result -> %s', result)

运行结果:

  1. 首先定义了两个顾客,一个是 Joe,他的积分是 0;另一个是 Ann,她的积分是 1100。

  2. 接着定义了一个包含三件水果的购物车。

  3. 然后分别对 Joe 与 Ann,应用 FidelityPromo 策略,依据积分来确定折扣。因为 Ann 的积分超过了 1000,所以她最终只需要付款 39.9 即可。

  4. 其它示例也是根据所选择的策略,来计算最终的付款金额。

2 Python 函数实现方式

我们来改写之前的示例,使用函数方式来定义策略模式。

from collections import namedtuple

Customer = namedtuple('Customer', 'name fidelity')


class LineItem:

    def __init__(self, product, quantity, price):
        self.price = price
        self.quantity = quantity
        self.product = product

    def total(self):
        return self.price * self.quantity


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

    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(self)
        return self.total() - discount

    def __repr__(self):
        fmt = '<Order total: {:.2f} due: {:2f}>'
        return fmt.format(self.total(), self.due())


def FidelityPromo(order):
    '''有 1000 或以上积分的顾客,每个订单享 5% 折扣'''
    return order.total() * .05 if order.customer.fidelity >= 1000 else 0


def BulkItemPromo(order):
    '''同一订单中,单个商品的数量达到 20 个或以上,享 10% 折扣'''

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


def LargeOrderPromo(order):
    '''订单中的不同商品达到 10 个或以上,享 7% 折扣'''

    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)]

result = Order(joe, cart, FidelityPromo)
logging.info('result -> %s', result)

result = Order(ann, cart, FidelityPromo)
logging.info('result -> %s', result)

banana_cart = [LineItem('banana', 30, 5), LineItem('apple', 10, 1.5)]
result = Order(joe, banana_cart, BulkItemPromo)
logging.info('result -> %s', result)

long_order = [LineItem(str(item_code), 1, 1.0) for item_code in range(10)]
result = Order(joe, long_order, LargeOrderPromo)
logging.info('result -> %s', result)

运行结果与传统写法一致。因为无须定义抽象类,所以也不需要引入 ABC 模块;而且函数式的写法也比类的写法更加简洁。


策略类本质上就是处理并输出数据,如果也不需要维护内部状态,我们就可以使用函数来替换实现。函数式写法相比抽象类写法,会更加轻量。


  1. UML类图.
  2. abc — 抽象基类.
  3. 说说 Python 的具名元组.
  4. Luciano Ramalho (作者),安道,吴珂 (译者).流畅的Python[M].人民邮电出版社,2017:280-291.

本文链接: http://www.dtmao.cc/news_show_2000262.shtml

附件下载

相关教程

    暂无相关的数据...

共有条评论 网友评论

验证码: 看不清楚?