커맨드 패턴 (Command Pattern)
하나의 객체를 통해 여러 객체들에 명령을 해야할 때 사용되는 패턴
요청 내역을 객체로 캡슐화 하여 객체를 서로 다른 요청 내역에 따라 매개변수화
요청을 큐에 저장하거나 로그로 기록하거나 작업 취소 기능을 사용할 수 있다.
여러 객체들에 명령을 하기 위해서 명령을 하는 객체가 명령을 받는 객체들을 알아야한다.
예를들어 집안의 조명을 제어하는 LightController가 있다.
LightController는 각각 다른 조명들(MainLight, SubLight, BedRoomLight) 에 대한 제어를 한다.
하지만, 이렇게 클래스를 설계하면 LightController는 많은 객체와 의존성을 갖는 슈퍼 객체가 되어버리며, 제어해야되는 조명이 늘어날 수록 LightController는 비대해진다. 만약 조명을 끄는 메서드도 추가하고 조명의 밝기 단계도 조절해야 한다면? 끔직한 슈퍼 객체가 탄생한다.
커멘드 패턴에서는 이를 해결하기 위해 이러한 커멘드(요청)를 커멘드 객체로 캡슐화 해서 커멘드 객체에서 하는 일을 신경쓰지 않고 메서드를 수행하도록 한다. 다른 객체에 해야 되는 명령들은 Command 객체를 구현하는 클래스에서 정의한다.
위 그림과 같이 Command가 만들어지면 LightController는 해당 Command를 수행하는 executeCommand 메서드 하나로 모든 Light 들을 조작할 수 있게 된다.
Command 패턴을 사용하면 여러 객체에 명령을 하기 위해 모든 객체들에 대해 의존성을 가질 필요가 없게 된다. 단순히 Command 객체의 execute 메서드만으로 모든 객체들의 제어가 가능해진다.
기능 추가하기
- 상태를 저장해서 작업을 취소하는 undo 기능
- 한 번의 버튼 입력으로 여러가지 명령어 처리하는 Macro 기능
#include <iostream>
#include <vector>
using namespace std;
// class Light {
// public:
// void on();
// void off();
// };
// class Stereo {
// public:
// void on();
// void off();
// void setCD();
// void setVolume(int value);
// };
class Command{
public:
virtual void execute() {};
virtual void undo() {};
};
class LightOnCommand : public Command {
public:
LightOnCommand(Light* _light) {
light = _light;
}
void execute() override {
light->on();
}
void undo() {
light->off();
}
private:
Light* light;
};
class LightOffCommand : public Command {
public:
LightOffCommand(Light* _light) {
light = _light;
}
void execute() override {
light->off();
}
void undo() {
light->on();
}
private:
Light* light;
};
class StereoOnWithCDCommand : public Command {
public:
StereoOnWithCDCommand(Stereo* _stereo) {
stereo = _stereo;
}
void execute() override {
stereo->on();
stereo->setCD();
stereo->setVolume(5);
}
void undo() override {
stereo->off();
}
private:
Stereo* stereo;
};
class MacroCommand : public Command {
public:
MacroCommand(vector<Command*>& _commands) {
commands = _commands;
}
void execute() {
for(auto element : commands) {
element->execute();
}
}
void undo() {
for(auto element : commands) {
element->undo();
}
}
private:
vector<Command*> commands;
};
class RemoteControl {
public:
RemoteControl() {
Command* noCommand = new Command{}; // null Command
for(auto index = 0; index<7; ++index) {
offCommands[index] = noCommand;
onCommands[index] = noCommand;
}
undoCommand = noCommand;
}
void setCommand(int slot, Command* onCommand, Command* offCommand) {
offCommands[slot] = offCommand;
onCommands[slot] = onCommand;
}
void onButtonPushed(int slot) {
onCommands[slot]->execute();
undoCommand = onCommands[slot];
}
void offButtonPushed(int slot) {
offCommands[slot]->execute();
undoCommand = offCommands[slot];
}
void undoButtonPushed() {
undoCommand->undo();
}
private:
Command* onCommands[7];
Command* offCommands[7];
Command* undoCommand;
};
실습
// 게임 키보드 키 변경
#include <iostream>
#include <memory>
using namespace std;
// ------------Real Action------------
class Aiming {
public:
void AimingOnAction() { cout << "Character Aiming On" << endl; }
void AimingOffAction() { cout << "Character Aiming Off" << endl; }
};
class Rest {
public:
void RestStartAction() { cout << "Character Rest Start" << endl; }
void RestCancelAction() { cout << "Character Rest Finished" << endl; }
};
class ExitGame {
public:
void exitGameAction() { cout << "exit Game" << endl; }
};
// ------------------------------------
// ------------- Command -------------
class Command {
public:
Command(){}
virtual void execute() {}
virtual void undo() {}
};
class AimingCommand : public Command {
public:
void execute() override { aimingAction->AimingOnAction(); }
void undo() override { aimingAction->AimingOffAction(); }
protected:
shared_ptr<Aiming> aimingAction;
};
class RestCommand : public Command {
public:
void execute() override { restAction->RestStartAction(); }
void undo() override { restAction->RestCancelAction(); }
protected:
shared_ptr<Rest> restAction;
};
class ExitGameCommand : public Command {
public:
void execute() override { exitGameAction->exitGameAction(); }
void undo() override {}
protected:
shared_ptr<ExitGame> exitGameAction;
};
// ------------------------------------
// This KeyBoard is Toggle
// First Pushed OnCommand,
// Second Pushed OffCommand
struct Key {
public:
virtual void Pressed() {
if (bOnExecute){
command->undo();
bOnExecute = false;
}
else{
command->execute();
bOnExecute = true;
}
}
void SetCommand(shared_ptr<Command> _command) {
command = _command;
}
protected:
shared_ptr<Command> command;
bool bOnExecute;
};
struct Q : public Key {};
struct W : public Key {};
struct E : public Key {};
struct R : public Key {};
class KeyBoardSetting {
public:
KeyBoardSetting() {
q = make_shared<Q>();
w = make_shared<W>();
e = make_shared<E>();
r = make_shared<R>();
}
void PressedQButton() { q->Pressed(); }
void PressedWButton() { w->Pressed(); }
void PressedEButton() { e->Pressed(); }
void PressedRButton() { r->Pressed(); }
void ChangeQButtonCommand(shared_ptr<Command> _command) { q->SetCommand(_command); }
void ChangeWButtonCommand(shared_ptr<Command> _command) { w->SetCommand(_command); }
void ChangeEButtonCommand(shared_ptr<Command> _command) { e->SetCommand(_command); }
void ChangeRButtonCommand(shared_ptr<Command> _command) { r->SetCommand(_command); }
private:
shared_ptr<Key> q;
shared_ptr<Key> w;
shared_ptr<Key> e;
shared_ptr<Key> r;
};
int main() {
unique_ptr<KeyBoardSetting> keyBoard = make_unique<KeyBoardSetting>();
// 키보드 기본 값
shared_ptr<AimingCommand> aimingCommand = make_shared<AimingCommand>();
shared_ptr<RestCommand> restCommand = make_shared<RestCommand>();
keyBoard->ChangeQButtonCommand(aimingCommand);
keyBoard->ChangeWButtonCommand(restCommand);
keyBoard->PressedQButton();
keyBoard->PressedQButton();
keyBoard->PressedWButton();
keyBoard->PressedWButton();
// 사용자가 키보드 키 커스텀 함
cout << "------ customizing KeyBoard ------" << endl;
shared_ptr<ExitGameCommand> exitGameCommand = make_shared<ExitGameCommand>();
keyBoard->ChangeQButtonCommand(exitGameCommand);
keyBoard->PressedQButton();
}
'디자인 패턴' 카테고리의 다른 글
퍼사드 패턴 (Facade pattern) (0) | 2023.05.01 |
---|---|
어댑터 패턴 (Adaptor pattern) (0) | 2023.04.30 |
싱글톤 패턴 (Singleton Pattern) (0) | 2023.04.16 |
데코레이터 패턴 (Decorator pattern) (0) | 2023.04.02 |
옵저버 패턴 (0) | 2023.03.26 |