首页 > 编程语言 >流畅的python--第十章 使用一等函数实现设计 模式

流畅的python--第十章 使用一等函数实现设计 模式

时间:2024-06-11 10:44:48浏览次数:19  
标签:10 promo python Decimal 第十章 order -- Order self

经典的策略模式

示例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 的重构,把具体策略换成了简单的函数,而且去掉了抽象类
PromoOrder类也要修改,但改动不大。
示例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

相关文章

  • centOS安装python3
    CentOS7安装Python3在CentOS系统上安装Python3的步骤如下:首先,打开终端。安装必要的构建工具和库:sudoyumgroupinstall-y"DevelopmentTools"sudoyuminstall-yzlib-develbzip2-developenssl-develncurses-develsqlite-develreadline-develtk-develgdbm......
  • 学习ThreeJS
    创建第一个应用  使用ThreeJS进行编程的时候,都是在调用newThree().XXX来实现方法,让我们先根据官方文档创建一个demohttps://threejs.org/docs/index.html#manual/zh/introduction/Creating-a-scene那我们需要什么东西才能让这个场景build起来呢?一个相机(camera),一般是使......
  • Vue 打包 Error: error:0308010C:digital envelope routines::unsupported
    这个错误通常与Node.js的加密模块和OpenSSL版本有关出现这个错误是因为node.jsV17版本中最近发布的OpenSSL3.0,而OpenSSL3.0对允许算法和密钥大小增加了严格的限制,可能会对生态系统造成一些影响.js/app.8d066b51.jsfromTerserError:error:0308010C:digitalenveloperout......
  • OpenCV实战案例——校正+切边[C++]
    0.前言本文以实战案例为背景,讲述如何使用计算机图形学知识完成需求,实现最终效果。本文包含实战案例素材以及过程代码讲解,方便读者理解。1.案例需求某公司打算开发一款用于提取学生作业本的程序,学生用手机拍摄自己的作业上传到程序,程序进行处理最终提取出作业本区域方便老师批改......
  • 使用Druid替换springboot默认连接池HikariPool
    使用Druid替换springboot默认连接池HikariPool1.在pom文件中增加Druid依赖<dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.1.23</version></dependency>2.在a......
  • 「Java开发指南」如何使用Spring注释器实现Spring控制器?(一)
    本教程将引导您使用SpringAnnotator实现Spring控制器,标准Java类被添加到搭建项目中,SpringAnnotatorSpring启用Java类。虽然本教程的重点是Spring控制器,但是SpringAnnotator也可以用于Spring服务、组件和存储库。在本教程中,您将学习如何:创建一个Java类将类配置为Spring控制......
  • docker pull镜像加速
    dockerpull镜像,发现一些还能用的sudovim/etc/docker/daemon.json写入{"registry-mirrors":["https://ustc-edu-cn.mirror.aliyuncs.com/","https://ccr.ccs.tencentyun.com/","https://docker.m.daocloud.io/"]}重启dockersudose......
  • 杭州的 IT 崩盘了么?
    大家好,我是R哥。今天分享一个爽飞了的面试辅导case:这个杭州兄弟空窗期1个月+,面试了6家公司0Offer,不知道问题出在哪,难道是杭州的IT崩盘了么?报名面试辅导后,经过一个多月的辅导打磨,现在成功入职某上市公司,涨薪30%+,955工作制,不咋加班,还不卷。其他各项福利都不错,比如三......
  • 500BIO01 1MRB150005R1J嵌入式数字模块
    500BIO011MRB150005R1J嵌入式数字模块是指在嵌入式系统中用于处理和转换数字信号的硬件或软件组件。这些模块广泛应用于各种电子设备和系统中,包括单片机、ARM处理器、DSP等。嵌入式数字模。 这类模块通过RS232接口将数字信号调制成音频模拟信号进行传输。它在无线通信和数......
  • 【2024-06-08】连岳摘抄
    23:59正因为是一张白纸,才可以随心所欲地描绘地图。一切全在你自己。对你来说,一切都是自由的,在你面前是无限的可能。这可是很棒的事啊。我衷心祈祷你可以相信自己,无悔地燃烧自己的人生。                            ......