커맨드 패턴(Command Pattern) 은 요청(명령)을 객체의 형태로 캡슐화하여, 요청을 보내는 객체와 처리하는 객체를 서로 분리시키는 디자인 패턴이다.
즉, 특정 작업이나 요청을 하나의 객체로 만들어 두면 이 객체를 재사용하거나, 필요에 따라 요청 실행을 나중으로 미루거나, 취소하는 등의 작업을 쉽게 할 수 있다.
커맨드 패턴은 다음과 같은 목적과 장점을 가지고 있다.
커맨드 패턴은 주로 다음과 같은 구성 요소로 이루어져 있다.
| 요소 | 역할 |
|---|---|
| Command | 명령을 수행하는 데 필요한 인터페이스 |
| ConcreteCommand | Command를 구현한 실제 명령 객체 |
| Invoker | 명령 객체를 요청하고 실행하는 호출자 |
| Receiver | 실제 명령을 수행할 책임을 가진 객체 |
class ICommand {
public:
virtual void execute() = 0;
virtual void undo() = 0;
virtual ~ICommand() {}
};
class Light {
public:
void turnOn() {
std::cout << "불을 켭니다." << std::endl;
}
void turnOff() {
std::cout << "불을 끕니다." << std::endl;
}
};
class LightOnCommand : public ICommand {
private:
Light* light;
public:
LightOnCommand(Light* l) : light(l) {}
void execute() override {
light->turnOn();
}
void undo() override {
light->turnOff();
}
};
class LightOffCommand : public ICommand {
private:
Light* light;
public:
LightOffCommand(Light* l) : light(l) {}
void execute() override {
light->turnOff();
}
void undo() override {
light->turnOn();
}
};
class RemoteControl {
private:
ICommand* command;
public:
void setCommand(ICommand* c) {
command = c;
}
void pressButton() {
command->execute();
}
void pressUndo() {
command->undo();
}
};
int main() {
Light livingRoomLight;
LightOnCommand lightOn(&livingRoomLight);
LightOffCommand lightOff(&livingRoomLight);
RemoteControl remote;
// 불 켜기 명령 실행
remote.setCommand(&lightOn);
remote.pressButton();
// 불 끄기 명령 실행
remote.setCommand(&lightOff);
remote.pressButton();
// 직전 명령 취소 (불 켜기)
remote.pressUndo();
return 0;
}
복사편집
불을 켭니다.
불을 끕니다.
불을 켭니다.
게임에서도 커맨드 패턴이 자주 사용됩니다. 예를 들면:
커맨드 패턴이 유용하다고 해서 모든 요청을 무조건 객체로 만드는 것은 바람직하지 않다.
너무 많은 명령 객체 생성은 메모리 소모가 많아질 수 있고 단순한 기능에 사용하면 오히려 코드가 복잡해질 수 있기 때문이다.
즉, 명령의 실행, 취소, 재실행, 기록과 같은 특수한 관리가 필요한 경우 커맨드 패턴을 적극적으로 사용하는 것이 좋다.