728x90
반응형
프로그래밍시에 종종 사용하는 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 의 또 다른 응용에는 어떤 것들이 있을지 생각해보자.
[힌트] 배치 명령어, 실행취소
728x90
반응형
'개발 언어 > 소프트웨어 공학' 카테고리의 다른 글
| MVC (Mode View Controller) Pattern (0) | 2012.05.24 |
|---|---|
| 디자인 패턴 정리 (0) | 2012.05.24 |
| Observer Pattern (0) | 2012.05.24 |
| Singleton - 프로세스내에서 단 하나뿐인 전역클래스를 만들고 싶을때 (0) | 2012.05.24 |