프로그래밍 관련/디자인 패턴

[행동 패턴] 명령 패턴 - Command

QA Engineer - P군 2016. 9. 20. 16:02
반응형

[행동 패턴] 명령 패턴 - Command




[이미지 출처 : http://www.codeproject.com]


목적


요청 자체를 캡슐화하여 , 요청이 서로 다른 사용자를 매개변수로 만들고, 요청을 대기시키거나 로깅하며, 되돌릴 수 있는 연산을 지원하기 위해서 사용합니다.


활용


- 수행할 동작을 매개변수화 하고자 할때

- 서로 다른 시간에 요청을 명시하고, 저장하며, 실행하고 싶을 때,

- 실행 취소 기능을 지원하고 싶을 떄

- 기본적인 연산의 조합으로 상위 수준의 연산을 써서 시스템을 구조화 시키고 싶을 때


장점


- 명령을 여러 개 조합해서 복합 명령을 만들 수 있습니다.

- 새로운 Command 객체를 추가하기 쉽습니다.


단점


- 명령의 취소를 반복적으로 누적될 경우에 오류가 발생될 수 있습니다.

- 취소가 되지 않는 템플릿(인자가 없거나 시스템적으로 불가)한 것들에 유의가 필요합니다.



구현


[구현 A - 게임 프로그래밍 패턴 (취소 없음)]


1
2
3
4
5
6
7
8
class Command
{
 
public :
    virtual ~Command(){};
    virtual void execute(GameActor& actro) = 0;
};
 
cs


액터에게 지시 할 수 있도록 엑터를 참조하는 명령 추상 클래스를 정의합니다.

 

1
2
3
4
5
6
7
8
9
10
11
class JumpCommand : public Command
{
    
public:
 
    virtual void execute(GameActor& actor)
    {
        actor.Jump();
    }
 
};
cs


점프를 하게 된다면, 해당 엑터의 행동을 실행 시키는 클래스를 명령 클래스를 상속 받아 정의합니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class InputHandler
{
public :
    Command* handleInput();
 
private :
 
    Command* buttonX_;
    Command* buttonY_;
 
    Command* handeInput()
    {
        if (IsPressed(BUTTON_X)) return buttonX_;
        if (IsPressed(BUTTON_Y)) return buttonY_;
    }
};
cs


핸들러입니다. 핸들러에 각 버튼이 눌리면 해당하는 버튼의 execute 함수를 실행합니다.

엑터가 누구던지 해당하는 명령을 수행 할 수 있습니다.


1
2
3
4
5
6
    Command* command = inputHander.handleInput();
 
    if (command)
    {
        command->execute(actor);
    }
cs


[구현 B - 게임 프로그래밍 패턴]


1
2
3
4
5
6
7
8
class Command
{
 
public :
    virtual ~Command(){};
    virtual void execute() = 0;
    virtual void undo() = 0;
};
cs

Command 추상 클래스를 정의합니다. 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class MoveUnitCommand : public Command
{
 
private :
 
    Unit *unit_;
    int x_ ,y_;
    int beforeX, beforeY;
 
public : 
 
    MoveUnitCommand(Unit* unit, int x, int y)
        : unit_(unit), x_(x), y_(y), beforeX(0), beforeY(0)
    {
 
    }
 
    virtual void execute()
    {
        beforeX = unit_->GetX();
        beforeY = unit_->GetY();
        unit_->MoveTo(x_, y_);
    }
 
    virtual void undo()
    {
        unit_->MoveTo(beforeX, beforeY);
    }
};
 
cs


게임 명령 중에 이동이라는 명령의 클래스를 만들고 , Command 클래스를 상속 받습니다.

상속 받고 실행과 취소 함수를 정의합니다. 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class InputHandler
{
 
public:
 
    Command* handeInput()
    {
        Unit* unit = GetSelectedUnit();
 
        if (IsPressed(BUTTON_UP))
        {
            int destY = unit->GetY() - 1;
            return new MoveUnitCommand(unit, unit->GetX(), destY);
        }
        if (IsPressed(BUTTON_Y))
        {
            // 공격 , 줍기 등의 행동
            //...
                //
        }
 
        return nullptr;
    }
};
 
cs


마지막으로 핸들러에게 명령을 전달합니다.

중요한 것은 명령을 내릴 때 마다 새로운 인스턴트를 생성한다는 점 입니다.

이렇게 새로운 인스턴트를 생성하고 하나로 모아 처리하면, 한번에 여러 개의 취소 및 복구가 가능합니다.





 

반응형