[C/C++] 커맨드 패턴(Command Pattern)

할랑말랑·2026년 3월 20일

C/C++

목록 보기
31/45

커맨드 패턴(Command Pattern)

요청을 객체의 형태로 캡슐화하여 사용자가 보낸 요청을 나중에 이용할 수 있도록 메소드 이름, 매개변수 등 요청에 필요한 정보를 저장 또는 로깅, 취소할 수 있게 하는 패턴

  • Command(명령)
    실행할 동작을 추상화한 인터페이스(요청 자체를 객체로 만드는 것)
  • ConcreteCommand
    실제로 실행되는 기능을 구현(Command라는 인터페이스를 상속하여 구현)
  • Receiver(수신자)
    실제로 일을 하는 객체, Command가 호출하는 대상
  • Invoker(호출자)
    기능의 실행을 요청하는 객체(명령을 실행시키는 객체)

1. 특징

  • 요청의 캡슐화 : 명령을 수행하는 로직을 별도의 객체(Command)로 캡슐화하여 요청 처리 과정을 유연하게 관리
  • 명령을 호출하는 객체(Invoker)와 명령을 처리하는 객체(Receiver)가 서로 직접적인 의존성 없이, 명령 인터페이스만을 통해 상호작용
  • 기능 확장 및 관리 (Undo/Redo): 명령의 역행(un-do)이나 큐(Queue)에 담아 순차적으로 실행하거나, 로그에 기록하는 작업이 용이

간단한 캐릭터 스킬 예시

  • 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) 기능을 구현하는 등의 확장도 용이하다.

0개의 댓글