当前位置: 首页 > news >正文

设计模式Python版 命令模式(下)

文章目录

  • 前言
  • 一、命令队列的实现
  • 二、撤销操作的实现
  • 三、请求日志
  • 四、宏命令


前言

GOF设计模式分三大类:

  • 创建型模式:关注对象的创建过程,包括单例模式、简单工厂模式、工厂方法模式、抽象工厂模式、原型模式和建造者模式。
  • 结构型模式:关注类和对象之间的组合,包括适配器模式、桥接模式、组合模式、装饰模式、外观模式、享元模式和代理模式。
  • 行为型模式:关注对象之间的交互,包括职责链模式、命令模式、解释器模式、迭代器模式、中介者模式、备忘录模式、观察者模式、状态模式、策略模式、模板方法模式和访问者模式。

如果还没有阅读第一部分,可以点击这里进行回顾:《设计模式Python版 命令模式(上)》。现在,继续第二部分。

一、命令队列的实现

命令队列

  • 有时需要将多个请求排队。当一个请求发送者发送一个请求时,不止一个请求接收者产生响应,这些请求接收者将逐个执行业务方法,完成对请求的处理。此时,可以通过命令队列来实现。
  • 命令队列的实现方法有多种形式,其中最常用、灵活性最好的一种方式是增加一个CommandQueue类。CommandQueue类负责存储多个命令对象,而不同的命令对象可以对应不同的请求接收者。
"""命令队列"""


class CmdQueue:
    def __init__(self):
        self.cmds: list[Cmd] = []  # 存储命令队列

    def add_cmd(self, cmd: Cmd):
        if cmd not in self.cmds:
            self.cmds.append(cmd)

    def remove_cmd(self, cmd: Cmd):
        if cmd in self.cmds:
            self.cmds.remove(cmd)

    def execute(self):
        # 调用每一个命令对象的execute()方法
        for i in self.cmds:
            i.execute()


"""请求发送者"""


class Invoker:
    def __init__(self, cmd_queue: CmdQueue):
        self.cmd_queue = cmd_queue  # CmdQueue的引用

    def call(self):
        self.cmd_queue.execute()
  • 命令队列与批处理有点类似。批处理,顾名思义,可以对一组对象(命令)进行批量处理,当一个发送者发送请求后,将有一系列接收者对请求做出响应。命令队列可以用于设计批处理应用程序。

二、撤销操作的实现

在命令模式中,可以通过调用一个命令对象的execute()方法来实现对请求的处理。如果需要撤销(Undo)请求,可通过在命令类中增加一个逆向操作来实现。

  • 示例:一个简易计算器,该计算器可以实现简单的数学运算,还可以对运算实施撤销操作。
  • 计算器界面类CalculatorForm充当请求发送者,实现数据求和功能的加法类Adder充当请求接收者,界面类可间接调用加法类中的add()方法实现加法运算,并且提供可撤销加法运算的undo()方法。
  • 由于没有保存命令对象的历史状态,只能实现一步撤销操作。
"""请求发送者"""


class CalculatorForm:
    def __init__(self):
        self.cmd: AbstractCmd = None

    def compute(self, value: int):
        i = self.cmd.execute(value)
        print(f"执行运算,运算结果为:{i}")

    def undo(self):
        i = self.cmd.undo()
        print(f"执行撤销,运算结果为:{i}")


"""请求接收者"""


class Adder:
    def __init__(self):
        self.num = 0  # 初始值为0

    def add(self, value: int) -> int:
        # 每次将传入的值与num作加法运算,再将结果返回
        self.num += value
        return self.num


"""抽象命令类"""


class AbstractCmd:
    # 执行方法
    def execute(self, value: int) -> int:
        raise NotImplementedError

    # 撤销方法
    def undo(self) -> int:
        raise NotImplementedError


"""具体命令类"""


class AddCmd(AbstractCmd):
    def __init__(self):
        self.adder = Adder()

    def execute(self, value):
        # 调用加法类的加法操作
        self.value = value
        return self.adder.add(value)

    def undo(self):
        # 通过加一个相反数来实现加法的逆向操作
        return self.adder.add(-self.value)
  • 客户端代码
form = CalculatorForm()
cmd: AbstractCmd = AddCmd()
form.cmd = cmd
form.compute(10)
form.compute(5)
form.compute(10)
form.undo()
  • 输出结果
执行运算,运算结果为:10
执行运算,运算结果为:15
执行运算,运算结果为:25
执行撤销,运算结果为:15

三、请求日志

请求日志就是将请求的历史记录保存下来,通常以日志文件(Log File)的形式永久存储在计算机中。

  • 很多系统都提供了日志文件,例如Windows日志文件、Oracle日志文件等。请求日志文件常用功能如下:
    • 一旦系统发生故障,日志文件可以为系统提供一种恢复机制。
    • 请求日志也可以用于实现批处理。
    • 可以将命令队列中的所有命令对象都存储在一个日志文件中。每执行一个命令则从日志文件中删除一个对应的命令对象,防止因为断电或者系统重启等原因造成请求丢失。而且可以避免重新发送全部请求时造成某些命令的重复执行。
  • 在实现请求日志时,可以将命令对象通过序列化写到日志文件中

示例:将对网站配置文件的操作请求记录在日志文件中,如果网站重新部署,只需要执行保存在日志文件中的命令对象即可修改配置文件。

from pathlib import Path
import pickle


"""请求发送者"""


class ConfigSettingWindow:
    # 配置文件设置窗口
    def __init__(self):
        self.cmds: list[Cmd] = []  # 存储每一次操作时的命令对象
        self.cmd: Cmd = None

    def call(self, args):
        # 执行配置文件修改命令,同时将命令对象添加到命令集合中
        self.cmd.execute(args)
        self.cmds.append(self.cmd)

    def save(self):
        # 将命令集合写入日志文件
        FileUtil.write_cmds(self.cmds)

    def recover(self):
        # 从日志文件中提取命令集合,并遍历调用每一个命令对象的execute()方法来实现配置文件的重新设置
        cmds = FileUtil.read_cmds()
        for i in cmds:
            i.execute(i.args)


"""请求接收者"""


class ConfigOperator:
    # 配置文件操作
    def insert(self, args: str):
        print(f"增加新节点:{args}")

    def modify(self, args: str):
        print(f"修改节点:{args}")

    def delete(self, args: str):
        print(f"删除节点:{args}")


"""抽象命令类"""


class Cmd:
    def __init__(self, name: str):
        self.name = name
        self.config_operator: ConfigOperator = None  # 请求接收者对象的引用
        self.args = None

    def execute(self, args):
        raise NotImplementedError


"""具体命令类"""


class InsertCmd(Cmd):
    # 增加
    def __init__(self, name):
        super().__init__(name)

    def execute(self, args):
        self.args = args
        self.config_operator.insert(args)


class ModifyCmd(Cmd):
    # 修改
    def __init__(self, name):
        super().__init__(name)

    def execute(self, args):
        self.args = args
        self.config_operator.modify(args)


class DeleteCmd(Cmd):
    # 删除
    def __init__(self, name):
        super().__init__(name)

    def execute(self, args):
        self.args = args
        self.config_operator.delete(args)


"""工具类:文件操作"""


class FileUtil:
    @staticmethod
    def write_cmds(cmds: list[Cmd]):
        # 将命令集合写入日志文件
        try:
            file = Path("command_config.log")
            contents = pickle.dumps(cmds)
            file.write_bytes(contents)
        except Exception as e:
            print("命令保存失败!")
            print(e)

    @staticmethod
    def read_cmds() -> list[Cmd]:
        # 从日志文件中提取命令集合
        try:
            file = Path("command_config.log")
            contents = file.read_bytes()
            cmds = pickle.loads(contents)
            return cmds
        except Exception as e:
            print("命令读取失败!")
            print(e)
  • 客户端代码
if __name__ == "__main__":
    csw = ConfigSettingWindow()  # 定义请求发送者
    co = ConfigOperator()  # 定义请求接收者
    # 4次对配置文件进行更必
    cmd = InsertCmd("增加")
    cmd.config_operator = co
    csw.cmd = cmd
    csw.call("网站首页")

    cmd = InsertCmd("增加")
    cmd.config_operator = co
    csw.cmd = cmd
    csw.call("端口号")

    cmd = ModifyCmd("修改")
    cmd.config_operator = co
    csw.cmd = cmd
    csw.call("网站首页")

    cmd = ModifyCmd("修改")
    cmd.config_operator = co
    csw.cmd = cmd
    csw.call("端口号")

    print("#" * 20)
    print("保存配置")
    csw.save()

    print("#" * 20)
    print("恢复配置")
    csw.recover()
  • 输出结果
增加新节点:网站首页
增加新节点:端口号
修改节点:网站首页
修改节点:端口号
####################
保存配置
####################
恢复配置
增加新节点:网站首页
增加新节点:端口号
修改节点:网站首页
修改节点:端口号

四、宏命令

宏命令

  • 宏命令(Macro Command)又称为组合命令,它是组合模式和命令模式联用的产物。
  • 宏命令是一个具体命令类,它拥有一个集合属性,在该集合中包含了对其他命令对象的引用。
  • 当调用宏命令的execute()方法时,将递归调用它所包含的每个成员命令的execute()方法。一个宏命令的成员可以是简单命令,还可以继续是宏命令。
  • 执行一个宏命令将触发多个具体命令的执行,从而实现对命令的批处理。
  • 宏命令结构图如下

在这里插入图片描述


您正在阅读的是《设计模式Python版》专栏!关注不迷路~

相关文章:

  • Keysight E5071C (Agilent) 网络分析仪的特性和规格
  • DeepSeek 本地部署(电脑安装)
  • 笔试题笔记#6 模拟三道题和总结知识
  • CTF-web:java-h2 堆叠注入rce -- N1ctf Junior EasyDB
  • 消息中间件深度剖析:以 RabbitMQ 和 Kafka 为核心
  • vue2和vue3响应式区别最通俗易懂的理解
  • 图文教程 | 2024年IDEA安装使用教程,JDK简易下载方法
  • SpringBoot 统一功能处理
  • 面试经典150题——分治
  • SkyWalking 10.1.0 实战:从零构建全链路监控,解锁微服务性能优化新境界
  • element-ui时间组件同一个月内选择/30天内选择
  • AI 学习入门之概述篇
  • KEPServerEX 的接口类型与连接方式的详细说明
  • 基于和声搜索(Harmony Search, HS)的多中心点选址优化算法matlab仿真
  • Flutter_学习记录_动画的简单了解
  • 【华为OD机考】华为OD笔试真题解析(7)--基站维修工程师
  • 【Qt】实现定期清理程序日志
  • 排序算法详解、应用对比与C语言实现
  • 【AI学习】DeepSeek-R1-Distill的意义和影响
  • 【TI C2000】F28002x的系统延时、GPIO配置及SCI(UART)串口发送、接收
  • 世卫大会中国代表团:中国深入参与全球卫生治理,为构建人类卫生健康共同体贡献中国力量
  • 曾毓群说未来三年重卡新能源渗透率将突破50%,宁德时代如何打好换电这张牌
  • 上博东馆常设陈列入选全国博物馆“十大精品”
  • 海外市场,押注中国无人驾驶龙头
  • 新华时评:博物馆正以可亲可近替代“高冷范儿”
  • 王伟妻子人民日报撰文:81192,一架永不停航的战机