命令模式
定义
命令模式(Command Pattern)是一种行为设计模式,它将请求封装为独立的对象,从而允许用户参数化其他对象,并支持请求的排队、记录、撤销/重做等操作。该模式通过解耦请求的发送者和接收者,提高了系统的灵活性和可扩展性。
核心思想
- 将请求封装成对象:将每个操作(如“打开文件”“保存数据”)抽象为命令对象,包含执行操作所需的所有信息。
- 解耦调用者与接收者:调用者(如按钮、菜单)无需知道具体操作如何执行,只需触发命令对象;接收者(如文档、设备)负责实际执行操作。
模式结构
命令模式包含以下角色:
-
命令接口(Command)
定义执行操作的抽象方法(如execute()
),通常包含撤销方法(如undo()
)。 -
具体命令(Concrete Command)
- 实现命令接口,关联一个接收者(Receiver)。
- 在
execute()
中调用接收者的具体方法(如receiver.action()
)。
-
接收者(Receiver)
真正执行操作的对象(如“灯”执行“打开”或“关闭”)。 -
调用者(Invoker)
触发命令的对象(如按钮、遥控器),持有命令对象并调用其execute()
。 -
客户端(Client)
创建具体命令对象,并设置其接收者和调用者。
工作流程
- 客户端创建命令对象,并关联接收者。
- 调用者持有命令对象,并在适当时机(如按钮点击)调用
execute()
。 - 具体命令调用接收者的方法,完成实际操作。
- (可选)命令记录状态以支持撤销/重做。
代码示例
以“遥控器控制灯”为例:
using System; // 1. 命令接口(定义执行和撤销操作) public interface ICommand { void Execute(); void Undo(); } // 2. 接收者:电灯(实际执行操作的对象) public class Light { public void TurnOn() => Console.WriteLine( "电灯已打开" ); public void TurnOff() => Console.WriteLine( "电灯已关闭" ); } // 3. 具体命令:开灯命令 public class TurnOnCommand : ICommand { private Light _light; public TurnOnCommand(Light light) { _light = light; } public void Execute() => _light.TurnOn(); public void Undo() => _light.TurnOff(); } // 4. 具体命令:关灯命令 public class TurnOffCommand : ICommand { private Light _light; public TurnOffCommand(Light light) { _light = light; } public void Execute() => _light.TurnOff(); public void Undo() => _light.TurnOn(); } // 5. 调用者:遥控器(触发命令的对象) public class RemoteControl { private ICommand _command; // 设置命令 public void SetCommand(ICommand command) { _command = command; } // 执行命令 public void PressButton() { _command.Execute(); } // 撤销命令 public void PressUndo() { _command.Undo(); } } // 6. 客户端代码 class Program { static void Main() { // 创建接收者(电灯) Light light = new Light();
// 创建具体命令并关联接收者 ICommand turnOn = new TurnOnCommand(light); ICommand turnOff = new TurnOffCommand(light);
// 创建调用者(遥控器) RemoteControl remote = new RemoteControl();
// 执行开灯命令 remote.SetCommand(turnOn); remote.PressButton(); // 输出:电灯已打开 remote.PressUndo(); // 输出:电灯已关闭 // 执行关灯命令 remote.SetCommand(turnOff); remote.PressButton(); // 输出:电灯已关闭 remote.PressUndo(); // 输出:电灯已打开 } }
代码说明
-
命令接口
ICommand
定义Execute()
和Undo()
方法,所有具体命令必须实现这两个操作。 -
接收者
Light
实际执行操作的对象,提供TurnOn()
和TurnOff()
方法。 -
具体命令
TurnOnCommand
/TurnOffCommand
- 关联接收者
Light
,并在Execute()
中调用其方法。 Undo()
实现反向操作(如开灯的撤销是关灯)。
- 关联接收者
-
调用者
RemoteControl
- 持有命令对象,通过
PressButton()
触发命令执行。 - 通过
PressUndo()
触发撤销操作。
- 持有命令对象,通过
-
客户端
Program
- 组合接收者、命令和调用者。
- 演示命令的链式调用(执行与撤销)。
扩展场景:宏命令(批量操作)
// 定义宏命令(组合多个命令) public class MacroCommand : ICommand { List _commands = new List(); public void AddCommand(ICommand command) { _commands.Add(command); } public void Execute() { foreach ( var cmd in _commands) { cmd.Execute(); } } public void Undo() { // 反向撤销所有命令 foreach ( var cmd in _commands.AsEnumerable().Reverse()) { cmd.Undo(); } } } // 客户端使用宏命令 static void Main() { Light light = new Light(); ICommand turnOn = new TurnOnCommand(light); ICommand turnOff = new TurnOffCommand(light); // 创建宏命令(开灯 -> 关灯 -> 开灯) MacroCommand macro = new MacroCommand(); macro.AddCommand(turnOn); macro.AddCommand(turnOff); macro.AddCommand(turnOn); RemoteControl remote = new RemoteControl(); remote.SetCommand(macro); Console.WriteLine( "执行宏命令:" ); remote.PressButton(); // 输出: // 电灯已打开 // 电灯已关闭 // 电灯已打开 Console.WriteLine( "撤销宏命令:" ); remote.PressUndo(); // 输出: // 电灯已关闭 // 电灯已打开 // 电灯已关闭 }
应用场景总结
- GUI 按钮/菜单:将点击事件绑定到命令对象。
- 事务管理:记录操作历史以实现撤销/重做。
- 任务队列:将命令存入队列异步执行。
- 批处理操作:通过宏命令组合多个操作。
命令模式在 C# 中广泛应用于 WPF 的 ICommand
接口(如 RelayCommand
)、ASP.NET Core 中间件等场景,是解耦请求与处理的经典设计模式。