# Why
命令模式可以将请求发送者和接收者完全解耦,发送者与接收者之间没有直接引用关系,发送请求的对象只需要直到如何发送请求,不必知道如何完成请求。
命令模式(Command Pattern):将一个请求封装为一个对象,从而让我们可用不同的请求对客户进行参数化;对请求排队或者记录请求日志,以及支持可撤销的操作。别名动作(Action)模式或事务(Transaction)模式。
# What
# UML
# 角色说明:
ConcreteCommand(具体命令类):对应具体的接收者对象,将接收者对象的动作绑定其中。
Invoker(调用者):即请求发送者,通过命令对象来执行请求。
每一个具体命令类对应一个请求的处理者(接收者),通过向请求发送者注入不同的具体命令对象可以使得相同的发送者对应不同的接收者,从而实现“将一个请求封装为一个对象,用不同的请求对客户进行参数化”。
# 命令队列
将多个请求排队,当一个请求发送者发送一个请求时,将不止一个请求接收者产生响应,这些请求接收者将逐个执行业务方法,完成对请求的处理。此时,可以使用命令队列。代码示例如下。
abstract class Command
{
public abstract void Execute();
}
class CommandQueue
{
private List<Command> _commands = new List<Command>();
public void AddCommand(Command command)
{
_commands.Add(command);
}
public void RemoveCommand(Command command)
{
_commands.Remove(command);
}
public void Execute()
{
_commands.ForEach(command => command.Execute());
}
}
class Invoker
{
private CommandQueue _commandQueue;
public Invoker(CommandQueue commandQueue)
{
_commandQueue = commandQueue;
}
public void SetCommandQueue(CommandQueue commandQueue)
{
_commandQueue = commandQueue;
}
public void Call()
{
_commandQueue.Execute();
}
}
# 撤销操作
可以通过逆向操作来实现撤销,还可以通过保存对象的历史来实现撤销,后者可以使用备忘录模式(Memento Pattern)。代码示例如下:
public class AddCommand : Command
{
private readonly Adder _adder = new Adder();
private int _lastValue = 0;
public override void Execute(int value)
{
var result = _adder.Add(value);
_lastValue = value;
Console.WriteLine("Add Result: {0}", result);
}
public override void Undo()
{
var result = _adder.Add(-_lastValue);
Console.WriteLine("Undo Result: {0}", result);
}
}
public class Adder
{
private int _sum = 0;
public int Add(int num)
{
_sum += num;
return _sum;
}
}
class Starter : IStart
{
public void Run()
{
var c = new AddCommand();
c.Execute(1);
c.Undo();
c.Execute(10);
c.Execute(15);
}
}
可以引入命令集合或其他方式存储每一次操作时命令的状态,从而实现多次撤销(Undo)和多次恢复(Redo)操作。
# 请求日志
即将请求的历史记录保存下来,通常以日志文件(Log File)的形式永久存储在计算机中。
# 宏命令
又称组合命令,组合模式和命令模式联用,通常宏命令不直接与请求接收者交互,而是通过它的成员来调用接收者的方法。执行一个宏命令将触发多个具体命令的执行,从而实现对命令的批处理。