출처: http://blog.naver.com/kzh8055?Redirect=Log&logNo=140052836097

(출처에 첨부있음)

흠...

 

Command Pattern 정의 - [ Head First ] Design Pattern

 

커맨드 패턴을 이용하면 요구 사항을 객체로 캡슐화 할수 있으며 , 매개 변수를 써서
여러가지 다른 요구 사항을 집어 넣을수도 있다. 또한 요청 내역을 큐에 저장하거나

로그로 기록할수도 있으며, 작업 취소 기능도 지원 가능하다.

 

- 참고로 존댓말을 반말 체로 컨버팅했다. 훗...

 

커맨드 패턴의 대략적인 정의는 위와 같고

좀더 자세히 구현에 대해 얘기하자면

 

요구 사항을 캡슐화( Class 화 )한다는게 핵심이다.
받은 명령( Command )을 직접 처리는 객체를 리시버(Reciver)라하고
Command 인터페이스를 구체화한 Command 객체는 이 리시버 객체를 멤버로써
갖고 실제 기능을 수행한다
이때 캡슐화된 Command 객체를 직접 처라하는 부분은 인보커( Invoker)라고 한다. 

 

여기서 요구 사항, 즉 명령을 캡슐화( 클래스 화 ) 한다고 했다.

 

그럼 거기서 얻을 수있는 장점은 무엇이겠나?

 

뭐 당연한 얘기겠지만 일단 요구사항( 명령 ) 을 객체화 할수 있다고 했으므로

객체를 사용해서 얻을수 있는 모든 장점을 얻을수 있을것이다.

 

가령, 특정 객체를 관리하는 관리자 클래스에서 이 객체 화된 요구사항을 저장하고 있다가

일률적으로 다룰수 있게 된다.

 

 

 

커맨드 패턴의 기본 형태

 

일단 커멘드 패턴이 어떻게 구현되는가 살펴 보자.

 

*다른 패턴과 마찬가지로 다형성을 사용하기 위해 인터페이스( 혹은 추상 클래스 )를 이용한다.

 

우선 커맨드 인터페이스를 선언한다.

 

 

 

class ICommand

{

   virtual void Execute() = 0;

};

 

 

 

뭐 Command Pattern을 보니 '실제 명령'을 수행하게되는 함수는

보통 Execute() 라고 작명을 하더만.

 

 

참고로 현재 ICommand 인터페이스의 멤버 함수는 달랑 Execute() 만 나중에

기능 추가( Undo() 라던가 ) 할시에 늘려 주면 된다.

 

하여간 모든 커맨드 객체 는 ICommand 인터페이스를 상속 받게 된다.

 

이제 커맨드 객체를 구현해 봅시다.

커맨드 객체의 특징은 다음과 같다.

 

 

1. ICommand 인터페이스를 상속 받게 되고

2. 실제로 일을 수행하는 객체( 포인터 )를 멤버로 포함하게 된다.

 

 

 

보통 어떤 명령을 할때는 그 명령을 받고 그것을 수행할 대상이 있어야 할것이다.

2 번에서의 직접 일을 수행하는 객체 가 바로 그런 놈이다.

뭐 명령 자체가 그런거니까. 쩝...

이러한 객체들을 리시버( Reciver ) 라한다.

 

 

그럼 Command 객체를 구현하기 앞서 리시버, 즉 받은 명령에 대해

실제로 일을 수행하게 되는 객체를 정의해 보자

 

 

class CEnemy

{

   public:

      Move( int PosX, int PosY )

      {

         m_PosX = PosX; m_PosY = PosY;

      }

      ...

   private:

      int m_PosX;

      int m_PosY;   

};

 

 

가령 '나쁜 놈'이라는 클래스가 위와 같다고 하자.

유일하게 나타난 Move() 라는 함수는 PosX, PosY 라는 인자를 받는데

뭐 보다시피 나쁜놈을 해당 X, Y 위치로 옮겨놓는 하는 함수다.

 

그럼 여기서 커맨드 객체를 한번 정의해 봅시다.

이 커맨드 객체는 나쁜놈에게 움직이라는( Move ) 명령( 혹은 요구사항 )을 나타낸다.

 

 

class CommandMove : public ICommand

{

   public:

      virtual void Execute();

 

   public:

      CommandMove( CEnemy *pEnemy, int PosX, int PosY );

 

   private:

      CEnemy *m_pEnemy;             // 리시버 객체( 포인터 )

     

      int m_PosX;

      int m_PosY;

};

 

 

찬찬히 살펴 보면

왠지 익숙한 Execute() 함수는 ICommand로 부터 물려받게 돼고

이것은 반드시 재정의 해야한다.( 순수 가상 함수니까 )

하여간 이 Execute() 함수는 실제로 리시버( 여기선 나쁜놈 )가

실제로 하게 되는 일과 관련된 코드가 들어간다.

뭐 아마도 이렇게 되지 않을까 싶다.

 

void CommandMove::Execute()

{

   m_pEnemy->Move( m_PosX, m_PosY );

}

 

다음은 살펴 볼 녀석은 생성자.

 

 

인자로 CEnemy 객체의 포인터와 위치 값들을 받게 된다.

사실 일반적인 함수 정의 라면 위치 이동 명령을 내리기 위해선

Execute() 함수에 위치값을 인자로 받게하는것이 더 알맞게 보이지만

Execute() 는 어디까지 모든 커맨드 객체가 공유하게 될 공통의 인터페이스이므로

이 형태가 바뀌어서는 안된다.

그래서 이 커맨드 객체를 생성할시 인자로 위치 값을 받게 한뒤 커맨드 객체의

멤버 변수( m_PosX, m_PosY )에 저장해 놓고 이 값을 활용하는 것이다.

 

CommandMove( CEnemy *pEnemy, int PosX, int PosY );

{

   m_pEnemy = pEnemy;

        

   m_PosX = PosX;

   m_PosY = PosY;

}

 

하여간 커맨드 객체의 정의는 끝났다.

 

 

이제 마지막으로 이를 실제로 활용하는 방법이다.

 

...

 

CEnemy *pEnemy = new CEnemy;                                               // 나쁜 놈 객체를 생성

 

ICommand *pCommand = new CommandMove( pEnemy, 80, 55 );    // 커맨드 객체를 생성

                                                                                               // '나쁜놈을 80, 55위치로

                                                                                               // 이동 시켜라'라는 명령

 

pCommand->Execute();   // 실제 명령을 수행한다.

 

 

현재 상태에선 직접 CommandMove의 Execute() 함수를 직접 호출했지만

보통은 ICommand 객체들을 관리하는 클래스가 있고

그 안에서 명령을 내린다.( Execute() 함수를 호출 )

보통 그러한 객체를 인보커( Invoker )라 한다.

 

 

위에서 사용될 예제의 기본 모티브는 디자인 패턴( 하이텔 GMA 강좌 정리 ) 에서 따온것이다.

제대로 된 예제를 보고 싶은 인간들은 이 강좌를 살펴 보도록 해라.

단, 강의 방식이 살을 덧 붙이는 형태여서 순차적으로 학습하지 않는다면

이해하기가 쉽지 않을 것이다.

참고로  디자인 패턴( 하이텔 GMA 강좌 정리 )  강좌를 구할수 있는 곳은 '해골 책'으로

유명한 김용준 씨가 운영하는 http://www.3dstudy.net 의 강좌란이다.

 

 


Posted by 세모아
,