【设计模式】解析命令模式并附带一个可撤销重做的例子
命令模式(Command Pattern)
概念:
· 命令模式是一种行为型设计模式· 核心思想是 将请求(操作)封装成一个对象,从而让你可以将请求参数化、队列化、记录日志、支持撤销等操作。
UML结构:Invoker|ICommand|-------------------| | ConcreteCommandA ConcreteCommandB|Receiver
代码示例:
/// <summary> /// 命令模式接口 /// </summary> public interface ICommand { // 执行命令void Execute();// 撤销命令void Undo(); }/// <summary> /// 加法命令类 /// </summary> public class AddCommand : ICommand {public Calculator Calculator { get; set; } // 实际实现操作的对象public int Count { get; set; } = 0; // 操作数/// <summary>/// 构造函数/// </summary>/// <param name="calculator">实际实现操作的对象</param>/// <param name="count">操作数</param>public AddCommand(Calculator calculator, int count){this.Calculator = calculator;this.Count = count;}/// <summary>/// 执行该命令对应的操作/// </summary>public void Execute(){Calculator.Add(Count);}/// <summary>/// 撤销该命令对应的操作/// </summary>public void Undo(){Calculator.Subtract(Count);} }/// <summary> /// 乘法命令类 /// </summary> public class MultiplyCommand : ICommand {public Calculator Calculator { get; set; } // 实际实现操作的对象public int Count { get; set; } = 0; // 操作数/// <summary>/// 构造函数/// </summary>/// <param name="calculator">实际实现操作的对象</param>/// <param name="count">操作数</param>public MultiplyCommand(Calculator calculator, int Count){this.Calculator = calculator;this.Count = Count;}/// <summary>/// 执行该命令对应的操作/// </summary>public void Execute(){Calculator.Multiply(Count);}/// <summary>/// 撤销该命令对应的操作/// </summary>public void Undo(){Calculator.Divide(Count);}}/// <summary> /// 实际执行业务逻辑的类 /// </summary> public class Calculator {public int Value { get; private set; } = 0;public void Add(int amount) => Value += amount;public void Subtract(int amount) => Value -= amount;public void Multiply(int amount) => Value *= amount;public void Divide(int amount) => Value /= amount;public override string ToString() => Value.ToString(); }/// <summary> /// 命令管理类 /// </summary> public static class CommandManager {private static readonly object instanceLock = new object(); // 单例对象锁private static CommandManager instance;public static CommandManager Instance{private set => instance = value;get{if (instance == null){lock (instanceLock){if (instance == null){instance = new CommandManager();}}}return instance;}}private readonly Stack<ICommand> undoCommandStack = new(); // 撤销栈,存储了可以撤销的操作private readonly Stack<ICommand> redoCommandStack = new(); // 重做栈,存储了可以重做的操作/// <summary>/// 执行命令/// </summary>/// <param name="command">命令的对象实例</param>public void Execute(ICommand command){if (command == null)return;//将命令添加进撤销栈AddCommandToUndoStack(command);//执行该命令command.Execute();}/// <summary>/// 撤销/// </summary>/// <returns></returns>public bool Undo(){ // 从撤销栈中获取命令ICommand command = GetCommandOnUndoStack();// 执行命令并返回对应的结果if (command != null){command.Undo();return true;}elsereturn false;}/// <summary>/// 重做/// </summary>/// <returns></returns>public bool Redo(){ // 从重做栈中获取对应的命令ICommand command = GetCommandOnRedoStack();// 执行命令并返回对应的结果if (command != null){command.Execute();return true;}elsereturn false;}/// <summary>/// 清空命令/// </summary>public void ClearCommand(){ // 清空撤销栈ClearCommandOnUndoStack();// 清空重做栈ClearCommandOnRedoStack();}/// <summary>/// 添加命令到撤销栈/// </summary>/// <param name="command">对应的命令</param>public void AddCommandToUndoStack(ICommand command){ // 成功添加命令时,同时也清空重做栈if (undoCommandStack != null){undoCommandStack.Push(command);ClearCommandOnRedoStack();}}/// <summary>/// 从撤销栈中获取对应的命令/// </summary>/// <returns>对应的命令</returns>public ICommand GetCommandOnUndoStack(){if (undoCommandStack != null && undoCommandStack.Count > 0){ICommand command = undoCommandStack.Pop();AddCommandToRedoStack(command);return command;}return null;}/// <summary>/// 清空撤销栈/// </summary>public void ClearCommandOnUndoStack(){if (undoCommandStack != null)undoCommandStack.Clear();}/// <summary>/// 添加命令到重做栈/// </summary>/// <param name="command">对应的命令</param>private void AddCommandToRedoStack(ICommand command){if (redoCommandStack != null)redoCommandStack.Push(command);}/// <summary>/// 从重做栈中获取到对应的命令/// </summary>/// <returns></returns>private ICommand GetCommandOnRedoStack(){if (redoCommandStack != null){ICommand command = redoCommandStack.Pop();AddCommandToUndoStack(command);return command;}return null;}/// <summary>/// 清空重做栈/// </summary>private void ClearCommandOnRedoStack(){if (redoCommandStack != null)redoCommandStack.Clear();} }public class Client {public static void Main(){Calculator calculator = new();ICommand addCommand = new AddCommand(calculator, 5);ICommand multiplyCommand = new MultiplyCommand(calculator, 5);CommandManager.Instance.Execute(addCommand);CommandManager.Instance.Execute(multiplyCommand);CommandManager.Instance.Undo();CommandManager.Instance.Redo();CommandManager.Instance.Execute(addCommand);} }
特点:
优点:
· 降低耦合:调用者只知道命令对象,不需要知道具体操作细节;
· 支持撤销和重做:因为命令对象封装了执行信息,可以存储历史命令;· 支持宏命令:可以把多个命令组合成一个命令(批量执行);
· 易扩展:新增命令只需添加新的
ConcreteCommand
,不影响原有系统;
缺点:
· 命令类数量增加:每个命令都需要一个具体类,类数可能较多;
· 增加系统复杂度:对于简单操作,引入命令模式会显得“重”;
· 学习成本:理解抽象层次和责任分离可能有一定难度;
适用场景:
· 需要 将请求调用者与执行者解耦;
· 需要 支持撤销/重做操作(如文本编辑器、绘图工具);
· 需要 将操作记录下来以便稍后执行(如任务队列、定时任务);
· 需要 支持宏命令/批量操作;· 需要 动态组合操作(如脚本化操作、事务命令);
举例:
· 文本编辑器的撤销/重做;
· 智能家居遥控器;