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
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;
}