命令模式

<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);
```