Skip to content

Head First 设计模式 06-命令模式

🏷️ 《Head First 设计模式》

命令模式 将“请求”封装成对象,以便使用不同的请求、队列或者日志来参数化其它对象。命令模式也支持可撤销的操作。

书中分别列举了 遥控器餐厅下单 两个例子。

我的理解是 命令模式 主要目的是为了解耦 触发任务的对象实际执行的对象 ,即 调用者Invoker)和 接收者Receiver)。

类图如下:

下面是模拟在 Client 中执行的测试代码。

csharp
Invoker invoker = new Invoker(); // 调用者
Receiver receiver = new Receiver(); // 命令接收者
ConcreteCommand command = new ConcreteCommand(receiver); // 创建命令
invoker.SetCommand(command); // 设置命令
invoker.Execute(); // 执行
invoker.Undo(); // 撤销

示例代码

Command

csharp
interface Command
{
    void Execute();
    void Undo();
}

Receiver

csharp
class Receiver
{
    public void Action()
    {
        Console.WriteLine("The receiver do an action.");
    }

    public void Undo()
    {
        Console.WriteLine("The receiver undo the action.");
    }
}

ConcreteCommand

csharp
class ConcreteCommand : Command
{
    Receiver _receiver;

    public ConcreteCommand(Receiver receiver)
    {
        _receiver = receiver;
    }

    public void Execute()
    {
        _receiver.Action();
    }

    public void Undo()
    {
        _receiver.Undo();
    }
}

Invoker

csharp
class Invoker
{
    Command _command;

    public void SetCommand(Command command)
    {
        _command = command;
    }

    public void Execute()
    {
        _command.Execute();
    }

    public void Undo()
    {
        _command.Undo();
    }
}

Client

csharp
class Client
{
    static void Main(string[] args)
    {
        Invoker invoker = new Invoker(); // 调用者
        Receiver receiver = new Receiver(); // 命令接收者
        ConcreteCommand command = new ConcreteCommand(receiver); // 创建命令
        invoker.SetCommand(command); // 设置命令
        invoker.Execute(); // 执行
        invoker.Undo(); // 撤销
        Console.ReadLine();
    }
}

宏命令

可以将 Invoker 中的 Command 修改为列表,来实现一次执行多个命令,实现类似于 宏命令 的效果。

csharp
class BathInvoker
{
    List<Command> _commands;

    public void SetCommand(Command command)
    {
        _commands.Add(command);
    }

    public void Execute()
    {
        foreach (var command in _commands)
        {
            command.Execute();
        }
    }

    public void Undo()
    {
        foreach (var command in _commands)
        {
            command.Undo();
        }
    }
}

更多用途

  1. 队列请求

    在宏命令的基础上,基于队列和实际的执行者之间是完全解耦的,可以实现类似于队列和消费的效果。

  2. 日志请求

    类似于 SQLServer 的日志,可以根据日志恢复数据库曾经执行过的操作。
    使用命令模式可以实现类似的效果,只需要追加 Store()Load() 方法。