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

Python命令模式介绍、使用

一、Python命令模式介绍

Python命令模式(Command Pattern)是一种行为型设计模式,它允许将请求或操作封装在对象中,并将其作为参数传递给调用对象,以在不同的环境中执行相同的请求或操作。

功能:

  • 将请求或操作与其接收者解耦,从而提高代码的灵活性和可重用性。
  • 支持撤销和重做操作,因为命令可以在历史记录中保存和管理。
  • 支持事务性操作,因为一组相关的命令可以组合成一个事务。

优点:

  • 命令模式可以使代码更加模块化和可扩展,因为命令可以轻松添加、删除或替换。
  • 命令模式使代码更加容易维护和测试,因为单个命令的代码只涉及一个操作,且命令可以在不同的环境中重用和测试。
  • 命令模式支持撤销操作,这增加了代码的可靠性和安全性。

缺点:

  • 命令模式的实现可能需要大量的代码,特别是当有许多不同的命令和接收者时。
  • 命令模式可能会导致代码的复杂性和间接性增加,因为它需要多个对象之间的交互。

应用场景:

  • 当需要将请求或操作封装在对象中并将其传递给调用对象时。
  • 当需要支持撤销和重做操作时。
  • 当需要支持事务性操作时。
  • 当需要动态添加、删除或替换命令时。

使用方式:

  • 创建一个命令接口,它包含执行和撤销方法。
  • 创建一个或多个命令类,它们实现命令接口中的方法,并包含命令相关的数据和操作。
  • 创建一个调用对象,它接收命令对象并将其存储在一些数据结构中,以便在需要时执行、撤销或重做它们。
  • 创建一个接收者对象,它实现命令类中的操作。
  • 命令类将接收者对象与操作相关联,并在执行或撤销时调用其方法。

在应用程序开发中的使用:

  • 撤销和重做操作:可以使用命令模式将受影响的操作保存在历史记录中,并支持撤销和重做操作。
  • 动态调用:可以使用命令模式将请求和调用对象解耦,从而允许动态配置调用对象。
  • 事务性操作:可以使用命令模式将一组相关的操作组合成一个事务,从而确保它们都成功或都失败。
  • 日志记录:可以使用命令模式将操作和结果记录在日志文件中,以便跟踪和调试应用程序。

二、命令模式使用

工作原理:

  • 调用对象接收命令对象,并将其存储在一个数据结构中,以便在需要时执行、撤销或重做它们。
  • 命令对象实现命令接口中的执行和撤销方法,并包含命令相关的数据和操作。
  • 调用对象将命令对象传递给接收者对象,它实现命令类中的操作。
  • 在执行时,命令对象调用接收者对象的方法以执行操作。在撤销时,命令对象调用接收者对象的方法以撤销操作。
  • 可以使用历史记录来存储命令对象,支持撤销和重做操作。

示例一:实现动态调用功能

在实现动态调用功能时,命令模式可以帮助我们实现很好的可扩展性和解耦。以下是一个简单的示例,其中我们使用命令模式来实现动态调用功能。

首先,我们定义一个命令接口,所有的命令都需要实现这个接口。

接下来,我们实现具体的命令,如下所示:

然后,我们需要有一个接收者来执行具体的操作。在这个例子中,我们不需要接收者。

接下来,我们定义一个 Invoker 类,它接收命令并在需要的时候调用它们。在这个例子中,我们使用 Invoker 来动态调用命令。

现在,我们可以使用 Invoker 来执行命令。

from abc import  ABC, abstractmethod# 定义抽象类:命令接口
class Command(ABC):@abstractmethoddef execute(self):pass# 定义实现类:实现具体命令
class test1Command(Command):def execute(self):print("test1 command")class test2Command(Command):def execute(self):print("test2 command")# 定义调用程序类
class Invoker:def __init__(self):self.commands = {}def set_command(self, name, command):self.commands[name] = command # 存入字典def execute_command(self, name):if name in self.commands:self.commands[name].execute()else:print(f"{name} not found!")# 创建实例
invoker = Invoker()invoker.set_command("test1", test1Command())
invoker.execute_command("test1")invoker.set_command("test2", test2Command())
invoker.execute_command("test2")invoker.execute_command("test")

运行结果:

test1 command
test2 command
test not found!

注意,在这个例子中,我们使用了 Invoker 来管理命令,并且使用命令的名称来动态调用命令。如果我们尝试执行没有添加到 Invoker 中的命令,那么我们就会收到一个错误消息。此外,我们可以使用 Invoker 来添加和删除命令,从而实现更好的可扩展性。

示例二:实现撤销和重做操作

在实现撤销和重做操作的场景中,命令模式可以实现很好的解耦和可扩展性。以下是一个简单的示例,其中我们使用命令模式来实现撤销和重做操作。

首先,我们定义一个命令接口,所有的命令都需要实现这个接口。

接下来,我们实现具体的命令,如下所示:

然后,我们需要有一个接收者来执行具体的操作。在这个例子中,我们定义了一个简单的列表类来实现添加和删除操作。

接下来,我们定义一个 Invoker 类,它接收命令并在需要的时候调用它们。

现在,我们可以使用 Invoker 来执行命令。

from abc import  ABC, abstractmethod# 定义抽象类:命令接口
class Command(ABC):@abstractmethoddef execute(self):pass@abstractmethoddef undo(self):pass@abstractmethoddef redo(self):pass# 定义实现类:实现具体命令
class AddCommand(Command):def __init__(self, receiver, value):self.receiver = receiverself.value = valuedef execute(self):self.receiver.add(self.value)def undo(self):self.receiver.remove(self.value)def redo(self):self.execute()class RemoveCommand(Command):def __init__(self, receiver, value):self.receiver = receiverself.value = valuedef execute(self):self.receiver.remove(self.value)def undo(self):self.receiver.add(self.value)def redo(self):self.execute()# 定义接收者
class Receiver():def __init__(self):self.data = []def add(self, item):self.data.append(item)print(f"Add item: {item}")def remove(self, item):self.data.remove(item)print(f"Remove item: {item}")def show(self):print("Current data: ", self.data)# 定义调用程序类
class Invoker:def __init__(self):self.commands = []self.index = -1def set_command(self,command):self.commands.append(command) # 存入数组self.index += 1def execute_command(self):self.commands[self.index].execute()def undo_command(self):if self.index >= 0:self.commands[self.index].undo()self.index -= 1def redo_command(self):if self.index < len(self.commands) -1:self.index += 1self.commands[self.index].redo()# 创建实例
receiver = Receiver()
invoker = Invoker()
print("1----add")
add_command = AddCommand(receiver, 1)
invoker.set_command(add_command)
invoker.execute_command()
receiver.show()
print("2----add")
add_command = AddCommand(receiver, 2)
invoker.set_command(add_command)
invoker.execute_command()
receiver.show()
print("3----remove")
remove_command = RemoveCommand(receiver, 1)
invoker.set_command(remove_command)
invoker.execute_command()
receiver.show()
print("4----undo")
invoker.undo_command()
receiver.show()
print("5----redo")
invoker.redo_command()
receiver.show()

运行结果:

1----add
Add item: 1
Current data:  [1]
2----add
Add item: 2
Current data:  [1, 2]
3----remove
Remove item: 1
Current data:  [2]
4----undo
Add item: 1
Current data:  [2, 1]
5----redo
Remove item: 1
Current data:  [2]

注意,在这个例子中,我们使用了 Invoker 来管理命令,并且每次调用 execute_command 方法时,都会执行当前命令。当我们需要执行撤销操作时,我们会调用 undo_command 方法,并将当前命令的索引减去 1。当我们需要执行重做操作时,我们会调用 redo_command 方法,并将当前命令的索引加上 1。如果我们已经达到了命令队列的末尾或开头,那么我们就不会执行任何操作。

实例三:实现日志记录功能

在实现日志记录功能时,命令模式可以帮助我们记录每个操作的详细信息,包括操作执行的时间、执行者、执行的结果等。以下是一个简单的示例,其中我们使用命令模式来实现日志记录功能。

首先,我们定义一个命令接口,所有的命令都需要实现这个接口。

每个命令需要实现 execute 方法和 undo 方法。execute 方法实际执行命令逻辑,undo 方法则实现命令的撤销操作。

接下来,我们实现具体的命令,定义了一个具体的命令AddCommand。这个命令需要一个接收者(receiver),它是负责实际执行操作的对象。在这个例子中,使用一个Receiver类来表示接收者。命令模式还提供了一个 log 方法,用于记录每个操作的详细信息。

使用 Invoker 来执行命令和撤销命令。Invoker可以维护一个命令队列,并同时执行多个命令。

在这个例子中,我们使用了 Invoker 来管理命令队列,并在需要时依次执行它们。我们还实现了一个 undo_commands 方法来撤销最后一个命令,并且在需要时打印当前状态。

from abc import ABC, abstractmethod# 定义逻辑类
class Command(ABC):@abstractmethoddef execute(self): # 实现执行命令逻辑pass@abstractmethoddef undo(self):   # 实现命令撤销pass# 定义具体实现类
class AddComand(Command):def __init__(self, receiver, value):self.receiver = receiver # 接收者self.value = valuedef execute(self):result = self.receiver.add(self.value) # 调用Receiver类的add()方法self.log(result)def undo(self):result = self.receiver.remove(self.value) # 调用Receiver类的remove()方法self.log(result)def log(self, result):print(f"added {self.value} to receiver {self.receiver} with result {result}")class RemoveComand(Command):def __init__(self, receiver, value):self.receiver = receiver # 接收者self.value = valuedef execute(self):result = self.receiver.remove(self.value) # 调用Receiver类的add()方法self.log(result)def undo(self):result = self.receiver.add(self.value) # 调用Receiver类的remove()方法self.log(result)def log(self, result):print(f"Remove {self.value} to receiver {self.receiver} with result {result}")# 定义接收者
class Receiver():def __init__(self):self.items = []def add(self, item):self.items.append(item)return Truedef remove(self, item):if item in self.items:self.items.remove(item)return Truereturn Falsedef __str__(self): # 定义对象的字符串表示形式return str(self.items)# 定义Invoker(调用程序)维护命令队列:执行命令、撤销命令
class Invoker():def __init__(self):self.commands = []self.undo_commands = []def set_command(self, command):self.commands.append(command)def execute_command(self):for command in self.commands:command.execute() # 调用具体实现类:执行方法execute()self.undo_commands.append(command)self.commands = []def undo_command(self):num_commands = len(self.undo_commands)if num_commands == 0:returncommand = self.undo_commands.pop() # pop()方法删除列表末尾元素,返回列表末尾元素。 pop(0)删除列表第一个元素。command.undo()   # 调用具体实现类:撤销方法undo()def show(self):for command in self.undo_commands:command.show()# 创建实例
receiver = Receiver()
invoke = Invoker()add_command = AddComand(receiver, 1)
invoke.set_command(add_command)
# invoke.execute_command()add_command = AddComand(receiver, 2)
invoke.set_command(add_command)
# invoke.execute_command()add_command = AddComand(receiver, 3)
invoke.set_command(add_command)invoke.execute_command()remove_command = RemoveComand(receiver, 2)
invoke.set_command(remove_command)
invoke.execute_command()print(receiver)

运行结果:

added 1 to receiver [1] with result True
added 2 to receiver [1, 2] with result True
added 3 to receiver [1, 2, 3] with result True
Remove 2 to receiver [1, 3] with result True

在这个例子中,我们首先添加三个项目到接收者中,然后删除一个项目。我们看到了添加操作的详细信息,包括操作执行的时间、执行者、执行的结果等。

在实际应用中,我们可以将每个命令的详细信息写入日志文件中,以便后续跟踪和调试。同时,我们还可以将日志信息发送到远程服务器上进行中央日志管理。这样可以帮助我们更好地管理和维护系统。

相关文章:

Python命令模式介绍、使用

一、Python命令模式介绍 Python命令模式&#xff08;Command Pattern&#xff09;是一种行为型设计模式&#xff0c;它允许将请求或操作封装在对象中&#xff0c;并将其作为参数传递给调用对象&#xff0c;以在不同的环境中执行相同的请求或操作。 功能&#xff1a; 将请求或…...

#typescript 使用file-saver模块#

场景&#xff1a;前端使用file-saver模块做导出文档的时候&#xff0c;出现两个错误 1&#xff1a;npm run build 提示找不到模块&#xff0c;如图 解决方法&#xff1a; 先卸载&#xff0c;不管是否安装都先要卸载 ,然后安装&#xff1a; npm uninstall file-saver npm…...

移动端适配布局rem和vw

在日益发展的移动互联网时代&#xff0c;作为前端开发者&#xff0c;我们必须了解和掌握各种移动端显示效果的适配技术。在众多适配方案中&#xff0c;使用rem和vw进行布局是当前最为流行和普遍使用的两种技术。通过合理运用这两种技术&#xff0c;我们可以让我们的网页在不同尺…...

【Java基础教程】(四十八)集合体系篇 · 上:全面解析 Collection、List、Set常用子接口及集合元素迭代遍历方式~【文末送书】

Java基础教程之集合体系 上 &#x1f539;本章学习目标1️⃣ 类集框架介绍2️⃣ 单列集合顶层接口&#xff1a;Collection3️⃣ List 子接口3.1 ArrayList 类&#x1f50d; 数组&#xff08;Array&#xff09;与列表&#xff08;ArrayList&#xff09;有什么区别?3.2 LinkedL…...

什么是 DNS ANAME 解析?

本人使用谷歌搜索了简中互联网&#xff0c;完全没有找到任何有关 ANAME 的文章……本文该不会是头一份吧 相信大家对于 DNS 的解析方式都不陌生&#xff0c;常见的有 A、CNAME、MX、TXT 记录等等。其中&#xff0c;网站常用的是 A 记录和 CNAME 记录&#xff1a;A 记录用于将域…...

Neo4j 集群和负载均衡

Neo4j 集群和负载均衡 Neo4j是当前最流行的开源图DB。刚好读到了Neo4j的集群和负载均衡策略&#xff0c;记录一下。 1 集群 Neo4j 集群使用主从复制实现高可用性和水平读扩展。 1.1 复制 集群的写入都通过主节点协调完成的&#xff0c;数据先写入主机&#xff0c;再同步到…...

go web框架 gin-gonic源码解读01————Engine

go web框架 gin-gonic源码解读01————Engine gin-gonic是go语言开发的轻量级web框架&#xff0c;性能优异&#xff0c;代码简洁&#xff0c;功能强大。有很多值得学习的地方,最近准备把这段时间学习gin的知识点&#xff0c;通过engine&#xff0c;context&#xff0c;router…...

windows版docker部署springcloud项目

材料&#xff1a; 1.windows版docker环境&#xff08;其他版教程可能道理一样但是比如文件后坠名上可能有差异&#xff09; 2.运行好的数据库容器&#xff08;实现教程&#xff09; 3.所有jar包 实现&#xff1a; 最后整好的文件夹结构图&#xff08;原工程文件机密&#xf…...

探索工程机械远程控制新纪元:Intewell-Hyper II震撼发布!

在当前的工程技术领域&#xff0c;远程控制技术以其卓越的效率和方便性&#xff0c;正受到越来越多的关注和运用。而在这个过程中&#xff0c;某机械集团以Intewell-HyperII操作系统为基础&#xff0c;打造出了具有前瞻性的工程机械远程控制器&#xff0c;为行业的发展提供了新…...

DM8 DSC集群实时主备搭建

1、环境准备 主库DSC集群公网ip&#xff1a;192.168.1.34/35 私有ip&#xff1a;192.168.10.134/135 备库ip&#xff1a;192.168.1.33 2、对DSC集群数据库全备 1)主库做全备 [dmdbadmdsc01 bin]$ disql sysdba/dameng123 BACKUP DATABASE TO WEEKLY_FULL_BAK BACKUPSE…...

配置IPv4 over IPv6隧道示例

IPv4 over IPv6隧道&#xff1a; 在IPv4 Internet向IPv6 Internet过渡后期&#xff0c;IPv6网络被大量部署后&#xff0c;而IPv4网络只是散布在世界各地的一些孤岛。利用隧道技术可以在IPv6网络上创建隧道&#xff0c;从而实现IPv4孤岛的互联&#xff0c;IPv4孤岛能通过IPv6公…...

在中国区部署日志通2.0

前提条件 一个域名&#xff1a;使用此域名来访问日志通控制台提供aws iam 的ssl证书 &#xff0c;而且必须跟域名相关联具有四个子网&#xff08;两个公有子网和两个私有子网&#xff09;和NAT网关的VPC 步骤 1.创建ACM证书 1.1 请求公有证书 1.2 配置域名 1.3 新申请的证书记…...

centos下安装jdk

环境:centos7/openjdk-8u40-b25 openJDK页面 java二进制包下载页面 华为jdk镜像 1.下载安装包后上传到服务器上&#xff0c;运行命令解压到/opt/目录下 tar cxvf server-jre-8u271-linux-x64.tar.gz -C /opt/2.配置环境变量 vi /etc/profile source /etc/profile添加下面的…...

【HDFS】LocatedBlocks、LocatedBlock、LocatedStripedBlock、ExtendedBlock类分析

本文主要介绍如下内容: 1、 介绍标题中类的功能及相关字段 2、 与字段初始化相关的一些细节 一、ExtendedBlock类 在Block Pools之间唯一标识一个块。 直白点就是一个Block再加一个块池id。 块池的概念是HDFS联邦集群之后产生的,因为一台DataNode的主机可以作为多个HDFS集群…...

Oracle 19c 报ORA-704 ORA-01555故障处理---惜分飞

异常断电导致数据库无法启动,尝试对数据文件进行recover操作,报ORA-00283 ORA-00742 ORA-00312错误,由于redo写丢失无法正常应用 D:\check_db>sqlplus / as sysdba SQL*Plus: Release 19.0.0.0.0 - Production on 星期日 7月 30 07:49:19 2023 Version 19.3.0.0.0 Copyrig…...

D356周赛复盘:滑动窗口+三元问题思路

文章目录 2798.满足目标工作时长的员工数目完整版 2799.统计完全子数组的数目&#xff08;滑动窗口&#xff09;思路完整版 2800.包含三个字符的最短字符串&#xff08;复用思路与三元问题思想&#xff09;思路复用减少字符串长度的思路为什么一次性操作两个字符串 完整版进一步…...

ETHERNET/IP 转ETHERCAT连接倍福和欧姆龙PLC的配置方法

ETHERNET/IP和ETHERCAT是两种不同的协议&#xff0c;它们在工业生产中都有广泛的应用。然而&#xff0c;由于协议不同&#xff0c;这两种设备之间无法通讯&#xff0c;这给工业生产带来了很大的麻烦。而捷米JM-EIP-ECAT网关应运而生&#xff0c;它能够连接到ETHERNET/IP总线和E…...

Git分布式版本控制工具和GitHub(一)--简介

一.Git概述 1.Git简介 【1】什么是Git? Git就是代码版本管理工具。 【2】为什么要使用Git &#xff08;1&#xff09;版本控制 写代码就是不断写BUG的过程&#xff08;当然我们是不会这么说的&#xff09;&#xff0c;很多时候你写了100行代码之后&#xff0c;突然醒悟&…...

【Terraform学习】Terraform-AWS部署快速入门(快速入门)

Terraform-AWS部署快速入门 实验步骤 连接到 Terraform 环境 SSH 连接到Terraform 环境(名为MyEC2Instance的实例) 在 Amazon Web Services &#xff08;AWS&#xff09; 上预置 EC2 实例 用于描述 Terraform 中基础结构的文件集称为 Terraform 配置。您将编写一个配置来定义…...

力扣75——深度优先搜索

总结leetcode75中深度优先搜索的算法题解题思路。 上一篇&#xff1a;力扣75——链表 以下代码部分为本人所写&#xff0c;部分为官方示例代码。 力扣75——深度优先搜索 1 二叉树的最大深度2 叶子相似的树3 统计二叉树中好节点的数目4 路径总和 III5 二叉树中的最长交错路径6 …...

【C++初阶】C++基础(上)——C++关键字、命名空间、C++输入输出、缺省参数、函数重载

目录 1. C关键字 2. 命名空间 2.1 命名空间的定义 2.2 命名空间的使用 3. C输入&输出 4. 缺省参数 4.1 缺省参数概念 4.2 缺省参数分类 5. 函数重载 5.1 函数重载概念 5.2 C支持函数重载的原理——名字修饰&#xff08;name Mingling&#xff09; 5.3 extern &…...

代码随想录训练营Day55动态规划part15|392.判断子序列|115.不同的子序列

392.判断子序列 编辑距离问题目前能够很简单的做出来&#xff0c;注意两个细节 s为空&#xff0c;直接输出true在break时&#xff0c;j不会再&#xff0c;因此在break前要手动 Carl用了二维数组&#xff0c;dp[i][j] 由dp[i-1][j-1]1dp[i][j-1]递推 115.不同的子序列 dp[i][…...

Linux下安装RabbitMQ教程

官方安装指南&#xff1a;https://www.rabbitmq.com/install-rpm.html 我们将要安装的RabbitMQ的版本是3.8.2 el/7/rabbitmq-server-3.8.2-1.el7.noarch.rpm - rabbitmq/rabbitmq-server packagecloud 不需要单独安装Erlang环境。 2. 环境配置&#xff1a; 前提&#xff…...

如何加强Mysql安全,请给出可行的具体措施

如何加强Mysql安全&#xff0c;请给出可行的具体措施 数据库对于公司而言是一个非常重要的资产。它在数据存储和管理、业务应用支持、决策和分析、数据安全和合规性、业务连续性以及客户关系管理等方面都发挥着关键作用。因此&#xff0c;公司应该高度重视数据库的建设、管理和…...

创造自己的宠物医院预约服务小程序,步骤详解

在现代社会&#xff0c;越来越多的人开始养宠物&#xff0c;而宠物的健康管理也成为了一个重要的话题。为了方便宠物主人随时随地进行宠物医院的管理和服务&#xff0c;开发一个宠物医院管理小程序是很有必要的。今天我们将分享一些制作宠物医院管理小程序的技巧&#xff0c;帮…...

MACOM EDI 需求分析

MACOM 是一家全球性半导体公司&#xff0c;专注于设计和制造高性能射频、微波和光电元件&#xff0c;其产品被广泛应用于通信、航空航天、国防、工业和医疗等领域。随着 MACOM 的不断发展&#xff0c;传统数据传输方式效率较低&#xff0c;无法满足 MACOM 的需求。为了提高企业…...

使用Spring Boot AOP实现日志记录

目录 介绍 1.1 什么是AOP 1.2 AOP体系与概念 AOP简单实现 2.1 新建一个SpringBoot项目&#xff0c;无需选择依赖 2.2 设置好本地Maven配置后&#xff0c;在pom.xml文件里添加添加maven依赖 2.3 创建一个业务类接口 2.4 在实体类实现接口业务 2.5 在单元测试运行结果 …...

图像中不规则物体的长轴与短轴:OpenCV实现指南

1.首先&#xff0c;读取图像并将其转换为灰度图像。 2.进行图像预处理&#xff0c;包括使用高斯模糊和阈值化&#xff0c;以便更好地处理图像。 3.通过使用OpenCV的cv2.findContours()函数&#xff0c;找到图像中的所有轮廓。 4.遍历所有轮廓&#xff0c;如果轮廓点的数量大…...

C/C++开发,opencv与qt结合播放视频

目录 一、qt_ui创建 1.1 ui设置 1.2 ui及代码输出保存 二、创建工程 2.1 工程目录及编译设置 2.2 源码设计 三、编译及测试 3.1 程序编译 3.2 程序运行 首先声明&#xff0c;这是一个OpenCV 3学习文档的案例&#xff0c;但是说明有些过于省略&#xff0c;只有一些简短的代码…...

磁共振图像处理中 fft1c 和 ifft1c 函数的 Python 实现

fft1c 和 ifft1c 是 MRI 图像处理的常用函数。通常使用如下的 Matlab 实现 &#xff08;Michael Lustig&#xff0c;2005&#xff09; function res ifft1c(x,dim)% res fft1c(x) % % orthonormal forward 1D FFT %nsize(x,dim); shftzeros(1,5); shft(dim)ceil(n/2);xcirc…...