프로그래밍시에 종종 사용하는 switch ~ case 구문의 가장 큰 단점은
case 구문이 늘어남에 따라 소스코드 가독성이 떨어진다는데에 있다.
물론 각각의 case 구문을 보기좋게 함수화 하거나 함수포인터를 적절히 활용한다면
어느정도 해결은 가능하다.
그리고 지금부터 소개할 Command Pattern을 사용해도 기존의 switch ~ case 구문을
보기 좋게 만들수 있다. 뿐만아니라 일련의 명령어들을 일관성있게 실행시키는 용도로도
커맨드 패턴은 자주 사용되어지는 편이다.
그렇다고 커맨드 패턴이 장점만 있는건 아니다.
예를 들면 switch ~ case 구문에서 case 구문이 몇 가지 안되는 경우에
커맨드 패턴을 사용하게 되면 오히려 프로그램의 복잡성만 증가한다는 단점이 있다.
#define CRTDBG_MAP_ALLOC #include <stdlib.h> #include <crtdbg.h> #include <iostream> #include <map> using namespace std; typedef enum ComamndId { NullCommand = 0, LoginCommand, LogoutCommand } CommandId; class ICommand { public: ICommand(){} virtual ~ICommand(){} virtual void Execute() = 0; virtual CommandId GetCommandId() = 0; }; class CNullCommand : public ICommand { public: CNullCommand(){} virtual ~CNullCommand(){} CommandId GetCommandId() { return NullCommand; } void Execute() { cout << "Null Comamnd " << endl; } }; class CLoginCommand : public ICommand { public: CLoginCommand(){} virtual ~CLoginCommand(){} CommandId GetCommandId() { return LoginCommand; } void Execute() { cout << "Login " << endl; } }; class CLogoutCommand : public ICommand { public: CLogoutCommand(){} virtual ~CLogoutCommand(){} CommandId GetCommandId() { return LogoutCommand; } void Execute() { cout << "Logout " << endl; } }; ComamndId DummyRequest() { return static_cast<CommandId>(rand() % 3); } void InitCommands(map<ComamndId, ICommand *> &cmds) { typedef map<ComamndId, ICommand *>::value_type Cmd; cmds.insert(Cmd(NullCommand, static_cast<ICommand *>(new CNullCommand))); cmds.insert(Cmd(LoginCommand, static_cast<ICommand *>(new CLoginCommand))); cmds.insert(Cmd(LogoutCommand, static_cast<ICommand *>(new CLogoutCommand))); // 나중에 명령어가 추가되면 이곳에 명령어를 추가함. } void UnInitCommands(map<ComamndId, ICommand *> &cmds) { map<CommandId, ICommand *>::iterator iter = cmds.begin(); for ( ; iter != cmds.end(); iter++ ) { ICommand *pCmd = static_cast<ICommand *>(iter->second); delete pCmd; } cmds.clear(); } ICommand *GetCommand(map<ComamndId, ICommand *> &cmds, CommandId cmdId) { map<CommandId, ICommand *>::iterator iter = cmds.find(cmdId); if ( iter == cmds.end() ) { iter = cmds.find(NullCommand); } return static_cast<ICommand *>(iter->second); } void TestCommandPattern() { int i = 0; map<ComamndId, ICommand *> cmds; InitCommands(cmds); for ( i = 0; i < 4; i++ ) GetCommand(cmds, DummyRequest())->Execute(); UnInitCommands(cmds); } int main(int argc, char **argv) { _CrtMemState before, after, diff; _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE); _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDOUT); _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE); _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDOUT); _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE); _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDOUT); _CrtMemCheckpoint(&before); TestCommandPattern(); _CrtMemCheckpoint(&after); if ( _CrtMemDifference(&diff, &before, &after) ) { _CrtMemDumpAllObjectsSince(NULL); _CrtMemDumpStatistics(&diff); system("pause"); } return 0; }
[생각할 꺼리]
1. switch ~ case 구문은 컴파일 시점에 모든 동작이 정의되어 있어야 하지만,
Command Pattern은 동적으로 동작을 추가하거나 삭제할 수 있다.
이와 같은 동작이 왜 가능한지 생각해보자.
2. Command Pattern을 switch ~ case 구문을 대체하는 용도로만 사용한다고 생각하고 있으면 안된다.
그럼, Command Pattern 의 또 다른 응용에는 어떤 것들이 있을지 생각해보자.
[힌트] 배치 명령어, 실행취소
'소프트웨어 > 소프트웨어 공학' 카테고리의 다른 글
MVC (Mode View Controller) Pattern (0) | 2012.05.24 |
---|---|
디자인 패턴 정리 (0) | 2012.05.24 |
Observer Pattern (0) | 2012.05.24 |
Singleton - 프로세스내에서 단 하나뿐인 전역클래스를 만들고 싶을때 (0) | 2012.05.24 |