요청을 객체의 형태로 캡슐화하여 사용자가 보낸 요청을 나중에 이용할 수 있도록 메소드 이름, 매개변수 등 요청에 필요한 정보를 저장 또는 로깅, 취소할 수 있게 하는 패턴
- Command : 실행할 동작을 추상화한 인터페이스
- ASkill,BSkill,CSkill,DSkillCommand : 실제로 실행되는 기능을 구현
- Character : 실제로 일을 하는 객체, Command가 호출하는 대상
- InputHandler : 기능의 실행을 요청하는 객체
#include <iostream> // 모든 커맨드 객체들이 구현해야 할 공통 인터페이스 (Command) class Command { public: virtual void execute() = 0; virtual ~Command() = default; }; // 요청을 실제로 처리하는 객체 (Receiver) class Character { public: void ASkill() { std::cout << "A스킬 발동" << std::endl; } void BSkill() { std::cout << "B스킬 발동" << std::endl; } void CSkill() { std::cout << "C스킬 발동" << std::endl; } void DSkill() { std::cout << "D스킬 발동" << std::endl; } }; // A스킬 사용 요청을 캡슐화하는 구체적인 커맨드 객체 (Concrete Command) class ASkillCommand : public Command { private: Character* character; // Receiver에 대한 참조 public: ASkillCommand(Character* _character) : character(_character) {}; void execute() override { character->ASkill(); // Receiver의 실제 행동을 호출 } }; // B스킬 사용 요청을 캡슐화하는 구체적인 커맨드 객체 class BSkillCommand : public Command { private: Character* character; public: BSkillCommand(Character* _character) : character(_character) {}; void execute() override { character->BSkill(); } }; // C스킬 사용 요청을 캡슐화하는 구체적인 커맨드 객체 class CSkillCommand : public Command { private: Character* character; public: CSkillCommand(Character* _character) : character(_character) {}; void execute() override { character->CSkill(); } }; // D스킬 사용 요청을 캡슐화하는 구체적인 커맨드 객체 class DSkillCommand : public Command { private: Character* character; public: DSkillCommand(Character* _character) : character(_character) {}; void execute() override { character->DSkill(); } }; // 요청을 받아 실행하는 객체 (Invoker) class InputHandler { private: public: void pressButton(Command* cmd) { // 어떤 커맨드인지는 모르지만, execute()를 호출하기만 하면 됨 cmd->execute(); } }; int main() { // 1. Receiver 객체 생성 Character c; // 2. Concrete Command 객체들 생성. Receiver를 인자로 넘겨줌. ASkillCommand askill(&c); BSkillCommand bskill(&c); CSkillCommand cskill(&c); DSkillCommand dskill(&c); // 3. Invoker 객체 생성 InputHandler input; // 4. Invoker에게 커맨드 객체를 전달하여 실행 요청 // 'D'키를 눌렀다고 가정 -> dskill 커맨드를 전달 input.pressButton(&dskill); return 0; }
- 요청의 캡슐화 : ASkillCommand 객체는 'c라는 캐릭터가 ASkill을 사용해야 한다'는 요청에 필요한 모든 정보(대상 객체 c와 실행할 메서드 ASkill)를 스스로 가지고 있다.
- 발동자(Invoker)와 수신자(Receiver)의 분리 : InputHandler는 Character 클래스의 존재를 전혀 모른다. ASkill, BSkill 같은 구체적인 스킬 이름도 알 필요가 없다. 그저 Command 인터페이스를 통해 execute()라는 메서드를 호출할 뿐이다.
- 유연성 : 이러한 분리 덕분에 실행 시간에 버튼의 기능을 쉽게 바꿀 수 있다. 예를 들어, input.pressButton(&askill);로 바꾸기만 하면 같은 버튼이 A스킬을 사용하게 된다. 또한, 커맨드 객체들을 큐에 저장하여 순차적으로 실행하거나, 로그로 남기거나, 실행 취소(Undo) 기능을 구현하는 등의 확장도 용이하다.