반응형

http://www.devbb.net/viewtopic.php?f=36&t=1086


Command 패턴


의도요청을 객체로 캡슐화함으로써 서로 다른 요청으로 클라이언트를 파라미터화하고, 요청을 저장하거나 기록을 남겨서 오퍼레이션의 취소도 가능하게 한다.

요청받은 오퍼레이션이 무엇이며, 이를 처리할 객체가 누구인지에 대한 아무런 정보 없이도 임의의 객체에게 메시지를 보내야 할 때가 있다.

Command 패턴은 메시지 자체를 객체로 처리함으로써 명시되지 않은 애플리케이션 객체의 요청을 처리할 수 있도록 지원한다. 메시지 객체는 다른 객체처럼 저장될 수 있고 전달될 수도 있다.


활용성
  • 수행할 행위 자체를 객체로 파라미터화하고자 할 때 Command 패턴을 사용한다. 절차지향 프로그램에서는 이를 콜백(callback) 함수로 사용했었다. 즉 어딘가에 등록해 두었다가 나중에 호출되도록 한 콜백 함수와 동일한 방식이다.
  • 서로 다른 시간에 요청을 명시하고, 저장하고, 수행할 수 있다. Command 객체는 원래의 요청과는 다른 생명 주기를 갖는다. 요청을 받아서 처리하는 객체는 호출될 함수의 주소 지정 방식과는 독립적으로 정의되어 있기 때문에 Command 객체를 다른 프로세스에 넘겨주고 해당하는 처리를 하게 할 수 있다.
  • 명령어 객체 자체에 실행 취소에 필요한 상태를 저장할 수 있다.
  • 변경 과정에 대한 로그를 남겨 두면 시스템이 고장났을 때 원상태로 복구가 가능하다.
  • 기본적인 오퍼레이션의 조합으로 좀더 고난이도의 오퍼레이션을 구성할 수 있다. 정보 시스템의 알반적인 특성은 트랜잭션을 처리해야 한다는 것이다. 트랜잭션은 일련의 과정을 통해 데이터를 변경하는 것인데, Command 패턴은 이런 트랜잭션의 모델링을 가능하게 한다. Command 클래스는 일관된 인터페이스를 정의하고 있는데, 이로써 모든 트랜잭션이 동일한 방식으로 호출된다. 새로운 트랜잭션을 만들면 상속에 의해 Commnad 클래스를 확장하면 되므로 시스템의 확장도 어렵지 않다.


구조
Attachment:
command.PNG


참여객체
  • Command : 오퍼레이션 수행에 필요한 인터페이스 선언
  • ConcreteCommand : Receiver 객체와 액션 간의 연결성을 정의한다. 또한 처리 객체에 정의된 오퍼레이션을 호출하도록 Execute를 구현한다.
  • Client : ConcreteCommand 객체를 생성하고 처리 객체로 정의한다.
  • Invoker : 명령어에게 처리를 수행할 것을 요청한다.
  • Receiver : 요청에 관련된 오퍼레이션 수행 방법을 알고 있다.

협력방법
  • 클라이언트는 ConcreteCommand 객체를 생성하고 이를 처리 객체로 지정한다.
  • Invoker 클래스는 ConcreteCommand 객체를 저장한다.
  • Invoker 클래스는 command 에 정의된 Execute() 함수를 호출한다. 명령어가 취소 가능한 것이라면 ConcreteCommand 는 이전에 Execute() 호출 전 상태의 취소 처리를 위해 저장한다.
  • ConcreteCommand 객체는 요청을 실제 처리할 객체에 정의된 오퍼레이션을 호출한다.


구현ConcreteCommand 서브클래스들은 수신 객체에 대한 참조자를 인스턴스 변수로 저장하고 있으며, 
이 수신 객체 정의된 메시지를 호출하도록 Execute() 를 구현함으로써 수신자와 처리 행위 간의 쌍을 정의한다. 
수신 객체는 자신에게 전달된 요청을 어떻게 처리해야 하는지를 알고 있다.
명령어가 지금까지 수행해 온 것을 뒤집는 오퍼레이션을 제공한다면 실행과 취소를 지원할 수 있다. 
이때 취소 프로세스 내에 오류가 누적되는 것은 피해야 한다.


예제코드Command 추상 클래스
Code:
class Command {
public:
   virtual ~Command();
   virtual void Execute() = 0;
protected:
   Command();
};

OpenCommand 클래스 : 선택한 이름의 문서를 여는 처리를 추상화한 객체.
Code:
class OpenCommand : public Command {
public:
   OpenCommand(Application* a)
   {
      _application = a;
   }
   virtual void Execute()
   {
      const char* name = AskUser();
      if( name != 0 ) {
         Document* doc = new Document(name);
         _application->Add(doc);
         doc->Open();
      }
   }
protected:
   virtual const char* AskUser();
private:
   Application* _application;
   char* _response;
};

PasteCommand 클래스 : 붙여 넣기 처리를 추상화한 객체
Code:
class PasteCommand : public Command {
public:
   PasteCommand(Document* doc)
   {
      _document = doc;
   }
   virtual void Execute()
   {
      _document->Paste();
   }
private:
   Document* _document;
};

명령어 취소나 특별한 아규먼트를 요청하지 않는 단순한 명령어라면 명령어의 처리 객체 자체를 파라미터로 하는 템플릿 클래스를 정의할 수 있다.
SimpleCommand 클래스는 Command의 서브클래스로, 처리 객체에 해당하는 Receiver 타입으로 파라미터화하여 처리 객체와 처리할 작업 간의 연결 관계를 관리한다.
Code:
template<class Receiver>
class SimpleCommand : public Command {
public:
   typedef void(Receiver::* Action)();
   SimpleCommand(Receiver* r, Action a) : _reciever(r), _action(a) { }
   virtual void Execute()
   {
      (_receiver->*action)();
   }
};

하나의 처리만을 다루는 것이 아니라 여러 가지 처리를 일련의 순서대로 수행해야 할 경우를 생략해서 MacroCommand 클래스를 정의한다.
MacroCommand 클래스는 일련의 명령어들을 처리하는 것으로서 일련의 명령어를 추가하거나 삭제하는 역할도 담당한다.
Code:
class MacroCommand : public Command {
public:
   MacroCommand();
   virtual ~MacroCommand();
   virtual void Add(Command* c)
   {
      _cmds->Append(c);
   }
   virtual void Remove(Command* c)
   {
      _cmds->Remove(c);
   }
   virtual void Execute()
   {
      for( auto it = _cmds.begin(); it != _cmds.end(); ++it ) {
         Command* c = (*it);
         c->Execute();
      }
   }
private:
   list<Command*> _cmds;
};


관련패턴MacroCommand 를 구현하고자 할 때 우리는 Composite 패턴을 사용하였다. 취소를 처리할 때 객체의 상태를 관리하고자 한다면 Memento를 사용해야 한다. 또 명령어가 처리되어 처리된 이력 리스트에 저장되기 전에 명령어를 복사해야 한다면 Prototype 패턴을 사용하면 된다.


_________________
외길을 가는 사람의 뒷모습은 아름답다.
 

 

 

반응형

+ Recent posts