命令模式

<image>

在物件导向程式设计的范畴中，命令模式（英语：Command pattern），是一种行为型设计模式，它是用来封装以后某个时间进行的一个动作或触发一个事件的所有信息的一个对象。这些信息包括方法名字，拥有这个方法的对象和给这个方法诸参数的诸值。

## 概述

使用命令对象，可以更容易的构造以通用构件，用来在其选定的时间，委托、序列或执行方法调用，而不需要知道这个方法的类或者这个方法的诸参数。使用调用者（invoker）对象，允许方便进行对命令执行的簿记，还有为命令实现不同的模态（mode），这是由调用者对象管理的，不需要让客户知晓簿记或模态的存在。

这个设计模式的中心想法，是紧密镜像函数式编程语言中的头等函数和高阶函数的语义。尤其调用者对象是高阶函数，而命令对象是其头等实际参数。

## 结构

<image>

在上面的UML类图中，`Invoker`类不直接实现一个请求。`Invoker`转而提及到`Command`接口，通过它来办理一个请求（`command.execute()`），这使得`Invoker`独立于这些请求的办理者。`Command1`类实现了`Command`接口，它在一个接收者（`receiver1.action1()`）上施行一个动作。此外，客户`Client`创建具体命令类比如`Command1`的对象并设置它的接收者。

UML序列图展示了运行时交互：`Invoker`对象调用`execute()`于`Command1`对象之上。`Command1`调用`action1()`在一个`Receiver1`对象之上，它办理这个请求。

## 范例

### C++

下面是基于《设计模式》书中前C++98实现的C++23实现例子：

```c++
import std;

using std::shared_ptr;
using std::unique_ptr;

// Abstract command
class Command {
protected:
    Command() = default;
public:
    // declares an interface for executing an operation.
    virtual void execute() = 0;
    virtual ~Command() = default;
};

// Concrete command
template <typename Receiver>
class SimpleCommand : public Command {
private:
    Receiver* receiver;
    Action action;
public:
    using Action = void (Receiver::*)();

    // defines a binding between a Receiver object and an action.
    SimpleCommand(shared_ptr<Receiver> receiver, Action action):
        receiver{receiver.get()}, action{action} {}

    SimpleCommand(const SimpleCommand&) = delete;
    const SimpleCommand& operator=(const SimpleCommand&) = delete;

    // implements execute by invoking the corresponding operation(s) on Receiver.
    virtual void execute() {
        (receiver->*action)();
    }
};

// Receiver
class MyClass {
public:
    // knows how to perform the operations associated with carrying out a request. Any class may serve as a Receiver.
    void action() {
        std::println("MyClass::action called");
    }
};

int main(int argc, char* argv[]) {
    shared_ptr<MyClass> receiver = std::make_shared<MyClass>();
    // ...
    unique_ptr<Command> command = std::make_unique<SimpleCommand<MyClass>>(receiver, &MyClass::action);
    // ...
    command->execute();
}
```

程序输出为：

```output
MyClass::action called
```

### Java

下面是Java例子：

```Java
import java.util.List;
import java.util.ArrayList;

/* The Command interface */
public interface Command {
    void execute();
}

/* The Invoker class */
public class Switch {
    private List<Command> history = new ArrayList<Command>();

    public Switch() {
    }

    public void storeAndExecute(Command cmd) {
        this.history.add(cmd); // optional 
        cmd.execute();        
    }
}

/* The Receiver class */
public class Light {
    public Light() {
    }

    public void turnOn() {
        System.out.println("The light is on");
    }

    public void turnOff() {
        System.out.println("The light is off");
    }
}

/* The Command for turning on the light - ConcreteCommand #1 */
public class FlipUpCommand implements Command {
    private Light theLight;

    public FlipUpCommand(Light light) {
        this.theLight = light;
    }

    public void execute(){
        theLight.turnOn();
    }
}

/* The Command for turning off the light - ConcreteCommand #2 */
public class FlipDownCommand implements Command {
    private Light theLight;

    public FlipDownCommand(Light light) {
        this.theLight = light;
    }

    public void execute() {
        theLight.turnOff();
    }
}

/* The test class or client */
public class PressSwitch {
    public static void main(String[] args){
        Light lamp = new Light();
        Command switchUp = new FlipUpCommand(lamp);
        Command switchDown = new FlipDownCommand(lamp);

        Switch mySwitch = new Switch();

        try {
            if ("ON".equalsIgnoreCase(args[0])) {
                mySwitch.storeAndExecute(switchUp);
            }
            else if ("OFF".equalsIgnoreCase(args[0])) {
                mySwitch.storeAndExecute(switchDown);
            }
            else {
                System.out.println("Argument \"ON\" or \"OFF\" is required.");
            }
        } catch (Exception e) {
            System.out.println("Arguments required.");
        }
    }
}
```

### C#

下面是C#例子：

```CSharp
using System;
using System.Collections.Generic;

namespace CommandPattern {
    public interface ICommand {
        void Execute();
    }

    /* The Invoker class */
    public class Switch {
        private List<ICommand> _commands = new List<ICommand>();

        public void StoreAndExecute(ICommand command) {
            _commands.Add(command);
            command.Execute();
        }
    }

    /* The Receiver class */
    public class Light {
        public void TurnOn() {
            Console.WriteLine("The light is on");
        }

        public void TurnOff() {
            Console.WriteLine("The light is off");
        }
    }

    /* The Command for turning on the light - ConcreteCommand #1 */
    public class FlipUpCommand : ICommand {
        private Light _light;

        public FlipUpCommand(Light light) {
            _light = light;
        }

        public void Execute() {
            _light.TurnOn();
        }
    }

    /* The Command for turning off the light - ConcreteCommand #2 */
    public class FlipDownCommand : ICommand {
        private Light _light;

        public FlipDownCommand(Light light) {
            _light = light;
        }

        public void Execute() {
            _light.TurnOff();
        }
    }

    /* The test class or client */
    internal class Program {
        public static void Main(string[] args) {
            Light lamp = new Light();
            ICommand switchUp = new FlipUpCommand(lamp);
            ICommand switchDown = new FlipDownCommand(lamp);

            Switch s = new Switch();
            string arg = args.Length > 0 ? args[0].ToUpper() : null;
            if (arg == "ON") {
                s.StoreAndExecute(switchUp);
            }
            else if (arg == "OFF") {
                s.StoreAndExecute(switchDown);
            }
            else {
                Console.WriteLine("Argument \"ON\" or \"OFF\" is required.");
            }
        }
    }
}
```

### Python

下面是Python例子：

```python
from abc import ABC, abstractmethod

class Light():
    def __init__(self, name='light_object'):
        self.name = name
    def turn_on(self):
        print("The light is on")
    def turn_off(self):
        print("The light is off")   

class Command(ABC):
    @abstractmethod
    def __init__(self):
        self.m = lambda self: None
    @abstractmethod
    def __call__(self): pass
    def bookkeeping(self):
        return (type(self).__name__, self.m.__self__.name)

class FlipUpCommand(Command):
    def __init__(self, receiver):
        self.m = receiver.turn_on
    def __call__(self):
        self.m()
        
class FlipDownCommand(Command):
    def __init__(self, receiver):
        self.m = receiver.turn_off
    def __call__(self):
        self.m()

class Switch():
    def __init__(self):
        self.history = []   
    def __call__(self, command):
        assert isinstance(command, Command)
        self.history += [command.bookkeeping()]
        command()
    def history(self):
        return self.history

class PressSwitch():
    def __init__(self):
        receiver = Light('lamp')
        self.flip_up_cmd = FlipUpCommand(receiver)
        self.flip_down_cmd = FlipDownCommand(receiver)
        self.invoker = Switch() 
    def __call__(self, cmd_line):
        cmd = cmd_line.strip().upper()
        if cmd == "ON":
            self.invoker(self.flip_up_cmd)
        elif cmd == "OFF":
            self.invoker(self.flip_down_cmd)
        else:
            print("Argument 'On' or 'Off' is required.")
    def history(self):
        return self.invoker.history
```

任何类只要定义了`__call__()`方法即可使其实例成为可调用对象。下面是其执行：

```pycon
>>> press_switch = PressSwitch()
>>> press_switch("On")
The light is on
>>> press_switch("Off")
The light is off
>>> press_switch("ThirdState")
Argument 'On' or 'Off' is required.
>>> press_switch.history()
[('FlipUpCommand', 'lamp'), ('FlipDownCommand', 'lamp')]
```

### Javascript

下面是Javascript例子：

```Javascript
/* The Invoker function */
var Switch = function(){
    var _commands = [];
    this.storeAndExecute = function(command){
        _commands.push(command);
        command.execute();
    }
}

/* The Receiver function */
var Light = function(){
    this.turnOn = function(){ console.log ('turn on')};
    this.turnOff = function(){ console.log ('turn off') };
}

/* The Command for turning on the light - ConcreteCommand #1 */
var FlipUpCommand = function(light){
    this.execute = light.turnOn;
}

/* The Command for turning off the light - ConcreteCommand #2 */
var FlipDownCommand = function(light){
    this.execute = light.turnOff;
}

var light = new Light();
var switchUp = new FlipUpCommand(light);
var switchDown = new FlipDownCommand(light);
var s = new Switch();

s.storeAndExecute(switchUp);
s.storeAndExecute(switchDown);
```