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

112、23种设计模式之命令模式(20/23)

一、命令模式的定义

命令模式是一种行为型设计模式,它将一个请求封装为一个对象,从而使你可以用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。
核心思想:将命令的 ** 发出者(Invoker)和执行者(Receiver)解耦,中间通过命令对象(Command)** 进行沟通。命令对象封装了执行操作所需的所有信息。
四个关键角色
(1)命令(Command):定义执行操作的接口(通常是一个 Execute 方法)。
(2)具体命令(ConcreteCommand):实现 Command 接口,绑定一个接收者和一个具体操作。调用接收者的相应方法来完成命令的执行。
(3)接收者(Receiver):知道如何执行一个请求相关的操作,真正执行命令的对象。
(4)调用者(Invoker):持有一个或多个命令对象,并在某个时间点调用命令的 Execute 方法。它不关心命令的具体执行逻辑。

二、应用场景

命令模式在以下场景中特别有用:
(1)需要解耦请求发送者和接收者:当你希望发送请求的对象(如按钮)不需要知道接收请求的对象(如窗口)是谁,以及它是如何处理请求的。
(2)需要支持撤销(Undo)和重做(Redo)操作:命令对象可以存储执行操作所需的状态,以便在需要时撤销或重做。
(3)需要支持事务(Transaction):将多个命令组合成一个复合命令,要么全部执行,要么全部不执行。
(4)需要将请求排队或记录请求日志:调用者可以将命令对象保存起来,稍后执行,或者记录下来用于审计或回放。
(5)需要动态地指定和执行请求:可以在运行时动态地创建不同的命令对象并交给调用者执行。
生活中的例子:

  • 遥控器:你(调用者)按下按钮(具体命令),遥控器(命令接口)发送信号,电视(接收者)执行相应操作(开机、换台)。你不需要知道电视内部是如何实现的。
  • 点餐:顾客(调用者)告诉服务员(命令接口)要点什么菜(具体命令),服务员将订单交给厨房(接收者),厨房按照订单做菜。
  • 宏命令:在游戏中,你可以录制一系列操作(多个命令)为一个宏,之后一键执行(调用者调用这个复合命令)。

三、优缺点

1.优点

(1)解耦:彻底分离了请求的发送者和接收者,提高了系统的灵活性和可扩展性。
(2)可扩展性:很容易添加新的命令,只需创建新的 ConcreteCommand 类,而无需修改现有代码(符合开闭原则)。
(3)支持撤销 / 重做和事务:这是命令模式最强大的功能之一。
(4)命令可以被参数化、排队和记录:增加了系统的灵活性,可以实现更复杂的功能,如任务调度、日志审计等。
(5)便于测试:ConcreteCommand 对象可以独立于 Receiver 和 Invoker 进行单元测试。

2.缺点

(1)增加了类的数量:为每一个具体操作都创建一个 ConcreteCommand 类,可能会导致系统中类的数量急剧增加,增加了系统的复杂性。
(2)可能引入冗余:对于一些简单的命令,创建一个专门的类可能会显得有些繁琐。

四、C# 代码示例

我们以一个简单的计算器为例,实现加、减操作,并支持撤销功能。

1.角色定义

  • ICommand (命令接口):定义 Execute 和 Undo 方法。
  • AddCommand, SubtractCommand (具体命令):实现 ICommand 接口,分别执行加、减运算,并能撤销。
  • Calculator (接收者):负责实际的计算逻辑(加、减)。
  • User (调用者):持有命令对象,负责触发命令的执行和撤销。

2.代码实现

using System;
using System.Collections.Generic;// 1. 命令接口 (Command)
public interface ICommand
{void Execute();void Undo();
}// 2. 接收者 (Receiver)
// 计算器类,负责实际的计算逻辑
public class Calculator
{private int _currentValue = 0;public int CurrentValue => _currentValue;public void Add(int value){_currentValue += value;Console.WriteLine($"计算器: 执行加法,当前值为: {_currentValue}");}public void Subtract(int value){_currentValue -= value;Console.WriteLine($"计算器: 执行减法,当前值为: {_currentValue}");}
}// 3. 具体命令 (ConcreteCommand) - 加法命令
public class AddCommand : ICommand
{private readonly Calculator _calculator;private readonly int _valueToAdd;public AddCommand(Calculator calculator, int valueToAdd){_calculator = calculator ?? throw new ArgumentNullException(nameof(calculator));_valueToAdd = valueToAdd;}// 执行加法public void Execute(){_calculator.Add(_valueToAdd);}// 撤销加法,即执行减法public void Undo(){_calculator.Subtract(_valueToAdd);}
}// 3. 具体命令 (ConcreteCommand) - 减法命令
public class SubtractCommand : ICommand
{private readonly Calculator _calculator;private readonly int _valueToSubtract;public SubtractCommand(Calculator calculator, int valueToSubtract){_calculator = calculator ?? throw new ArgumentNullException(nameof(calculator));_valueToSubtract = valueToSubtract;}// 执行减法public void Execute(){_calculator.Subtract(_valueToSubtract);}// 撤销减法,即执行加法public void Undo(){_calculator.Add(_valueToSubtract);}
}// 4. 调用者 (Invoker)
// 用户类,负责发起命令和管理命令历史(用于撤销)
public class User
{private readonly Calculator _calculator;private readonly Stack<ICommand> _commandHistory = new Stack<ICommand>();public User(Calculator calculator){_calculator = calculator ?? throw new ArgumentNullException(nameof(calculator));}// 执行一个新命令public void ExecuteCommand(ICommand command){command.Execute();_commandHistory.Push(command);}// 撤销上一个命令public void UndoLastCommand(){if (_commandHistory.Count > 0){ICommand commandToUndo = _commandHistory.Pop();Console.WriteLine("\n用户: 请求撤销上一步操作...");commandToUndo.Undo();}else{Console.WriteLine("\n用户: 没有可撤销的操作。");}}public void ShowCurrentValue(){Console.WriteLine($"\n当前计算结果: {_calculator.CurrentValue}");}
}// 客户端代码
class Program
{static void Main(string[] args){// 创建接收者Calculator calculator = new Calculator();// 创建调用者,并将接收者传入User user = new User(calculator);Console.WriteLine("--- 开始计算 ---");user.ShowCurrentValue();// 用户执行一系列命令Console.WriteLine("\n用户: 输入 +5");user.ExecuteCommand(new AddCommand(calculator, 5)); // 5user.ShowCurrentValue();Console.WriteLine("\n用户: 输入 -2");user.ExecuteCommand(new SubtractCommand(calculator, 2)); // 3user.ShowCurrentValue();Console.WriteLine("\n用户: 输入 +10");user.ExecuteCommand(new AddCommand(calculator, 10)); // 13user.ShowCurrentValue();// 用户决定撤销操作user.UndoLastCommand(); // 回到 3user.ShowCurrentValue();user.UndoLastCommand(); // 回到 5user.ShowCurrentValue();user.UndoLastCommand(); // 回到 0user.ShowCurrentValue();// 再撤销就没了user.UndoLastCommand();Console.WriteLine("\n--- 计算结束 ---");}
}

3.输出结果

--- 开始计算 ---当前计算结果: 0用户: 输入 +5
计算器: 执行加法,当前值为: 5当前计算结果: 5用户: 输入 -2
计算器: 执行减法,当前值为: 3当前计算结果: 3用户: 输入 +10
计算器: 执行加法,当前值为: 13当前计算结果: 13用户: 请求撤销上一步操作...
计算器: 执行减法,当前值为: 3当前计算结果: 3用户: 请求撤销上一步操作...
计算器: 执行加法,当前值为: 5当前计算结果: 5用户: 请求撤销上一步操作...
计算器: 执行减法,当前值为: 0当前计算结果: 0用户: 请求撤销上一步操作...
用户: 没有可撤销的操作。--- 计算结束 ---

这个例子清晰地展示了命令模式如何将请求的发起者(User)和执行者(Calculator)解耦,并通过命令对象(AddCommand, SubtractCommand)实现了灵活的撤销功能。
在这里插入图片描述

http://www.dtcms.com/a/605052.html

相关文章:

  • 第6章:空间查询与地理处理
  • 使用 Docker Compose 一键更新:深入理解 docker-compose pull 的适用场景
  • 一次在VS2022中使用sqlite数据库故障排查过程
  • Mailjet Setup Pitfall Guide: SPF, DKIM, DMARC Deliverability
  • 最好的企业网站电子商务网站建设考试重点
  • 大学新校区建设网站北京seo方法
  • SPI学习(QA)
  • 怎么用数据仓库来进行数据治理?
  • Linux_6:FTP云盘项目
  • Spring Boot spring.factories文件详细说明
  • 网站seo文章免费asp地方门户网站系统
  • 《信息存储与管理》逻辑串讲
  • dify TTS部署 GPT-SoVITS
  • kotlin中SharedFlow的简单使用
  • Kotlin 中的 inline 和 reified 关键字
  • 开封府景点网站及移动端建设情况精品资源共享课网站建设 碧辉腾乐
  • 战场目标检测:Faster R-CNN与RegNetX-800MF融合实现建筑物人员坦克车辆识别_2
  • 易语言黑月编译器:提升编程效率与性能优化 | 深入解析易语言开发中的工具应用与技巧
  • Vibe Coding - 从Vibe Coding到Spec Coding_AI编码范式的进化之路
  • 宣化网站建设青岛网站制作推广平台
  • 【多模态大模型面经】 BERT 专题面经
  • Node.js 开发实战:从入门到精通
  • 草莓病害智能识别与分类_Cascade-RCNN_HRNetV2p-W18-20e_COCO实现
  • 改造多模块!!无法使用三方依赖的异常处理
  • JMeter 自动化实战:自动生成文件并传参接口的完整方案
  • AutoSAR实战:RTA-OS Counters操作系统计数器详解
  • FCAF3D: Fully Convolutional Anchor-Free 3D Object Detection论文精读
  • 北京市轨道交通建设管理有限公司网站企业网站建设合同书模板
  • 做图表的网站大连关键词
  • Vue 3中集成GIS(地理信息系统)