[Design Pattern] 명령 패턴(Command Pattern)의 기본 개념 정리 (C++ 샘플코드 포함)
명령(Command) 패턴이란?
명령 패턴은 요청을 객체로 캡슐화하여, 요청을 보낸 객체(Invoker)와 요청을 처리하는 객체(Receiver) 사이의 결합도를 낮추는 행위 디자인 패턴입니다. 이를 통해 요청을 큐에 저장하거나, 로깅하거나, 취소(Undo)/재실행(Redo)하는 등의 기능을 쉽게 구현할 수 있습니다.
마치 식당에서 웨이터가 손님의 주문을 받아 주방에 전달하는 것과 같습니다. 손님(Client)은 웨이터(Invoker)에게 주문(Command)을 하고, 웨이터는 주문 내용을 주방(Receiver)에 전달합니다. 손님은 주방이 어떻게 요리하는지 알 필요가 없고, 웨이터는 각 요리 방법을 알 필요 없이 주문만 전달하면 됩니다.
명령(Command) 패턴의 구성 요소
- Command (명령 인터페이스): 모든 구체적인 명령 클래스가 구현해야 하는 인터페이스 또는 추상 클래스입니다. Execute() 메서드를 선언하여 명령 실행을 정의합니다.
- Concrete Command (구체적인 명령): 실제 명령을 구현하는 클래스입니다. Receiver 객체에 대한 참조를 가지고 있으며, Execute() 메서드에서 Receiver의 특정 작업을 호출합니다.
- Receiver (수신자): 실제 작업을 수행하는 객체입니다. Concrete Command 객체에 의해 호출됩니다.
- Invoker (요청자): Command 객체를 가지고 있으며, Command의 Execute() 메서드를 호출하여 요청을 실행합니다. Invoker는 어떤 Concrete Command가 실행되는지 알 필요가 없습니다.
- Client (클라이언트): Concrete Command 객체를 생성하고, Receiver 객체를 연결하여 Invoker에 전달합니다.
명령(Command) 패턴의 작동 방식
- Client는 Concrete Command 객체를 생성하고, Receiver 객체를 연결합니다.
- Client는 생성된 Command 객체를 Invoker에 전달합니다.
- Invoker는 Command 객체의 Execute() 메서드를 호출합니다.
- Command 객체의 Execute() 메서드는 연결된 Receiver 객체의 특정 작업을 호출합니다.
명령(Command) 패턴의 장단점
장점
- 낮은 결합도: Invoker와 Receiver 사이의 결합도를 낮추어 코드의 유연성과 재사용성을 향상시킵니다.
- 요청 큐잉: 요청을 큐에 저장하여 순차적으로 처리하거나, 특정 조건에 따라 처리를 지연시킬 수 있습니다.
- 로깅 및 취소/재실행: 요청 내역을 로깅하거나, 실행 취소(Undo) 및 재실행(Redo) 기능을 쉽게 구현할 수 있습니다.
- 복합 명령: 여러 개의 명령을 하나의 복합 명령으로 묶어서 실행할 수 있습니다.
단점
- 클래스 증가: 명령의 종류가 많아질수록 클래스의 수가 증가할 수 있습니다.
활용 예시
- 메뉴 시스템: 각 메뉴 항목을 명령 객체로 캡슐화하여 메뉴 동작을 처리합니다.
- GUI 버튼: 버튼 클릭 이벤트를 명령 객체로 캡슐화합니다.
- 게임의 입력 처리: 각 키 입력 또는 마우스 클릭을 명령 객체로 캡슐화하여 게임 로직과 분리합니다.
- 텍스트 편집기의 Undo/Redo 기능: 각 편집 작업을 명령 객체로 캡슐화하여 Undo/Redo 스택에 저장합니다.
예시코드
#include <iostream>
#include <vector>
// 명령 인터페이스
class Command {
public:
virtual void Execute() = 0;
virtual ~Command() {}
};
// 수신자
class Light {
public:
void TurnOn() {
std::cout << "Light is ON" << std::endl;
}
void TurnOff() {
std::cout << "Light is OFF" << std::endl;
}
};
// 구체적인 명령: 전등 켜기
class TurnOnCommand : public Command {
private:
Light* light_;
public:
TurnOnCommand(Light* light) : light_(light) {}
void Execute() override {
light_->TurnOn();
}
};
// 구체적인 명령: 전등 끄기
class TurnOffCommand : public Command {
private:
Light* light_;
public:
TurnOffCommand(Light* light) : light_(light) {}
void Execute() override {
light_->TurnOff();
}
};
// 요청자
class RemoteControl {
private:
std::vector<Command*> commands_;
public:
void SetCommand(Command* command) {
commands_.push_back(command);
}
void PressButton() {
for(Command* command : commands_){
command->Execute();
}
}
~RemoteControl(){
for(Command* command : commands_){
delete command;
}
}
};
int main() {
Light light;
RemoteControl remote;
remote.SetCommand(new TurnOnCommand(&light));
remote.SetCommand(new TurnOffCommand(&light));
remote.PressButton();
return 0;
}