🎈 커맨드 패턴 (Command Pattern)
• 명령을 캡슐화하고 실행하기 위해 사용
• GUI 응용 프로그램, 트랜잭션 처리, 큐 관리 등에 활용 가능
• 명령 이력 관리, 역명령 실행(Undo), 동적 선택과 같은 상황에서 유용
• 디자인 패턴 중 행위 패턴에 해당된다
[예제 코드] → Command Pattern
#include <iostream>
#include <vector>
class Light {
public:
void turnOn() {
std::cout << "전원 on..." << std::endl;
}
void turnOff() {
std::cout << "전원 off..." << std::endl;
}
};
class Command {
public:
virtual void execute() = 0;
};
class TurnOnLightCommand : public Command {
private:
Light& light;
public:
TurnOnLightCommand(Light& l) : light(l) {}
void execute() {
light.turnOn();
}
};
class TurnOffLightCommand : public Command {
private:
Light& light;
public:
TurnOffLightCommand(Light& l) : light(l) {}
void execute() {
light.turnOff();
}
};
// Invoker (호출자) 클래스
class RemoteControl {
private:
std::vector<Command*> commands;
public:
void addCommand(Command* cmd) {
commands.push_back(cmd);
}
void pressButton(int index) {
if (index >= 0 && index < commands.size()) {
commands[index]->execute();
}
else {
std::cout << "유효하지 않은 버튼!" << std::endl;
}
}
};
int main() {
Light light;
TurnOnLightCommand turnOnCmd(light);
TurnOffLightCommand turnOffCmd(light);
RemoteControl remote;
remote.addCommand(&turnOnCmd);
remote.addCommand(&turnOffCmd);
remote.pressButton(0); // 전원 on.
remote.pressButton(1); // 전원 off.
remote.pressButton(5); // 유효하지 않은 버튼.
return 0;
}
전원을 켜고 끄는 리모컨을 구현하였다.
Command 패턴으로 리모컨 버튼과 명령을 캡슐화 처리
[예제 코드] → Command Pattern 적용 X
명령어 패턴을 적용하지 않는다면 어떻게 될까?
#include <iostream>
class Light {
public:
void turnOn() {
std::cout << "전원을 켭니다." << std::endl;
}
void turnOff() {
std::cout << "전원을 끕니다." << std::endl;
}
};
int main() {
Light light;
// 전원 켜기
light.turnOn();
// 전원 끄기
light.turnOff();
return 0;
}
Command 패턴을 적용하지 않는다면
리모컨 버튼을 누를 때마다 직접적으로 Light 객체 메서드 호출이 필요하다.
이는 리모컨 버튼과 전원 제어 코드가 강하게 결합된 셈이다.
🎈 커맨드 패턴 장단점
+ 확장성: 새롭게 명령 추가하거나 기존 명령어 변경이 쉽다.
+ Undo 실행: 명령 이력을 기록하고 취소를 쉽게 구현할 수 있다.
+ 클라이언트 코드 간소화: 명령 수행 객체 세부 내용을 알 필요가 없어진다.
- 클래스 수 증가: 명령마다 별도의 클래스가 필요하므로 클래스 수가 증가
- 복잡성 증가: 간단한 명령의 경우 패턴 적용이 더 복잡해 보인다.
[예제 코드] → Undo 명령어
#include <iostream>
#include <string>
#include <vector>
// Receiver (장치) 클래스: 텍스트 에디터
class TextEditor {
public:
void openDocument(const std::string& document) {
std::cout << "문서 열기: " << document << std::endl;
}
void closeDocument(const std::string& document) {
std::cout << "문서 닫기: " << document << std::endl;
}
};
// Command (명령) 인터페이스
class Command {
public:
virtual void execute() = 0;
};
// ConcreteCommand (구체적인 명령) 클래스
class OpenDocumentCommand : public Command {
private:
TextEditor& editor;
std::string document;
public:
OpenDocumentCommand(TextEditor& e, const std::string& doc) : editor(e), document(doc) {}
void execute() override {
editor.openDocument(document);
}
};
class CloseDocumentCommand : public Command {
private:
TextEditor& editor;
std::string document;
public:
CloseDocumentCommand(TextEditor& e, const std::string& doc) : editor(e), document(doc) {}
void execute() override {
editor.closeDocument(document);
}
};
// Invoker (호출자) 클래스
class TextEditorInvoker {
private:
std::vector<Command*> commandHistory;
public:
void executeCommand(Command* cmd) {
cmd->execute();
commandHistory.push_back(cmd);
}
void undoLastCommand() {
if (!commandHistory.empty()) {
Command* lastCommand = commandHistory.back();
lastCommand->execute(); // 역명령 실행
commandHistory.pop_back();
}
else {
std::cout << "명령 이력이 없습니다." << std::endl;
}
}
};
int main() {
TextEditor editor;
TextEditorInvoker invoker;
// 명령 생성
OpenDocumentCommand openCmd(editor, "example.txt");
CloseDocumentCommand closeCmd(editor, "example.txt");
// 명령 실행
invoker.executeCommand(&openCmd);
invoker.executeCommand(&closeCmd);
// 명령 실행 취소 (Undo)
invoker.undoLastCommand();
return 0;
}
TextEditorInvoker 클래스는 명령을 관리하고 실행하며
실행 이력을 유지하고 명령 실행 취소를 지원한다.
🎈 다른 디자인 패턴과 비교
• Observer
관찰자 패턴은 주체(Subject)와 관찰자(Observer)를 분리하여
주체의 상태 변화를 관찰자에게 알려주는 패턴으로
주로 상태 변경 알림 및 이벤트 처리에 사용된다.
커맨드 패턴은 실행 요청을 수신하고 처리할 객체에 전달하는 것에서 비슷한 점이 보인다.
• 전략 (Strategy)
전략 패턴은 알고리즘을 정의하고, 각각을 캡슐화하며 런타임시간에 교체 가능하다.
커맨드 패턴은 명령을 객체로 캡슐화하는 것에서는 비슷한점이 있다.
상태 패턴은 객체의 내부 상태를 변경하고
해당 상태에 따라 객체의 행동을 다르게 처리한다
Factory 패턴은 객체를 생성하기 위한 인터페이스를 정의하고
서브클래스에서 구체적인 생성 과정 결정
주로 객체 생성 및 초기화에 사용됩니다.
'까망 동네 > 디자인 패턴' 카테고리의 다른 글
[디자인패턴] 컴포지트 패턴 (Composite Pattern) (1) | 2023.11.05 |
---|---|
[디자인패턴] 인터프리터 패턴 (Interpreter Pattern) (2) | 2023.11.04 |
[디자인패턴] 장식자 패턴 (Decorator) (2) | 2023.11.01 |
[디자인패턴] 프로토타입 (Prototype Pattern) (1) | 2023.10.31 |
[디자인 패턴] 브릿지 패턴 (Bridge Pattern) (3) | 2023.10.28 |
댓글