Home [Design Patterns] Command Pattern
Post
Cancel

[Design Patterns] Command Pattern

Definition

The command pattern encapsulates a request as an object, thereby allowing you to parametrize other objects with different request queues and supporting undoable operations.

Scenario

When there are lots of commands passing around, we don’t want to hard-code inside the command invoker, and we also want to undo and redo it.

Class Diagram

Command Pattern Command Pattern

Instead of letting Invoker directly call and implement the thing we want to control (Light), we create ICommand and its concrete implementations (TurnOnCommand and TurnOffCommand) to encapsulate these commands so they can be passed around. TurnOnCommand and TurnOffCommand implement what each command should do and call the receiver (Light).

The invoker can be anything, like different controllers, but they can share the same commands or operations. For a button in a controller (Invoker), it stores a ICommand. If we keep track of the command queue list, we can undo them by calling Unexecute() in ICommand a reverse order.

The point is that we encapsulate something trivial (i.e. the commmand here), so that we can pass it around and queue.

Used Cases

  • Photo editing software can undo and redo a series of commands. We can queue the requests and have them executed or un-executed.
  • Customizing the function of the buttons on the remote controller by attaching different commands.

Implementation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
// command.h
#ifndef __COMMAND_H__
#define __COMMAND_H__

#include <iostream>
#include <memory>
#include <vector>

class Light {
public:
    Light() {}
    void On() { std::cout << "[Light] Turn on" << std::endl; }
    void Off() { std::cout << "[Light] Turn off" << std::endl; }
};

class ICommand {
public:
    virtual void Execute() = 0;
    virtual void Unexecute() = 0;
};

class TurnOnCommand : public ICommand {
private:
    std::unique_ptr<Light> light_;

public:
    TurnOnCommand(std::unique_ptr<Light>&& light);
    void Execute() override;
    void Unexecute() override;
};

class TurnOffCommand : public ICommand {
private:
    std::unique_ptr<Light> light_;

public:
    TurnOffCommand(std::unique_ptr<Light>&& light);
    void Execute() override;
    void Unexecute() override;
};

class Invoker {
private:
    std::vector<std::string> command_queue_;
    std::unique_ptr<ICommand> button1_;
    std::unique_ptr<ICommand> button2_;

public:
    Invoker(std::unique_ptr<ICommand>&& button1, std::unique_ptr<ICommand>&& button2);
    void ClickButton1();
    void ClickButton2();
    void UndoAll();
};

#endif  //__COMMAND_H__
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// command.cpp
#include "command.h"

TurnOnCommand::TurnOnCommand(std::unique_ptr<Light>&& light) { light_ = std::move(light); }
void TurnOnCommand::Execute() { light_->On(); }
void TurnOnCommand::Unexecute() {
    std::cout << "[Undo] ";
    light_->Off();
}

TurnOffCommand::TurnOffCommand(std::unique_ptr<Light>&& light) { light_ = std::move(light); }
void TurnOffCommand::Execute() { light_->Off(); }
void TurnOffCommand::Unexecute() {
    std::cout << "[Undo] ";
    light_->On();
}

Invoker::Invoker(std::unique_ptr<ICommand>&& button1, std::unique_ptr<ICommand>&& button2) {
    button1_ = std::move(button1);
    button2_ = std::move(button2);
}

void Invoker::ClickButton1() {
    button1_->Execute();
    command_queue_.emplace_back(std::string("button1_"));
}

void Invoker::ClickButton2() {
    button2_->Execute();
    command_queue_.emplace_back(std::string("button2_"));
}

void Invoker::UndoAll() {
    // probably have a better way besides storing strings.
    for (short i = command_queue_.size() - 1; i >= 0; i--) {
        if (command_queue_[i] == "button1_") {
            button1_->Unexecute();
        } else if (command_queue_[i] == "button2_") {
            button2_->Unexecute();
        }
    }
    command_queue_.clear();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// main.cpp
#include "command.h"

int main() {

    Invoker controller(std::make_unique<TurnOnCommand>(std::make_unique<Light>()),
                       std::make_unique<TurnOffCommand>(std::make_unique<Light>()));
    controller.ClickButton1();
    controller.ClickButton2();

    std::cout << "---------" << std::endl;
    controller.UndoAll();

    return 0;
}

Reference

This post is licensed under CC BY 4.0 by the author.