프로그램의 상태에 따라 처리할 게 다르다면 어떻게 처리해야 될까요? 제일 먼저 생각나는 방법은 분기문이네요. if, else 를 써서 각 state 에 따라 수행할 코드들을 쓰는 겁니다. 그런데 이렇게 하게 되면 시간이 지나면서 분기문이 소스코드 곳곳에 생기게 됩니다.
하나 하나 살펴보도록 하지요. 먼저 Context.Request() 라는 함수에서 콜되는 부분을 따라가면 아래처럼 분기문이 있었을 겁니다.
{
STATECODE State;
Request()
{
Handle();
}
Handle()
{
{
// Do some initialization works
}
else if (State == Running)
{
// Do some works for Running mode
}
else if (State == Idling)
{
// Do some works for Idling mode
}
}
}
분기문을 쓰는 함수가 Request() 하나 였다면 크게 문제가 없을 겁니다만, 보통은 코딩하다 보면 아래처럼 여러개가 생기곤 합니다.
{
STATECODE State;
Request()
{
Handle();
}
Handle()
{
{
// Do some initialization works
}
else if (State == Running)
{
// Do some works for Running mode
}
else if (State == Idling)
{
// Do some works for Idling mode
}
}
GetValue()
{
return 0;
else if (State == Running)
return 1;
else if (State == Idling)
return 2;
}
}
좀 단순하게 표현한 것이지만, 위의 GetValue() 함수 처럼 if-else 구문을 포함하는 게 늘어날 수 있다는 의미입니다. 이걸 없애기 위해서 State pattern 을 도입합니다. State pattern 은 이 if-else 구문을 상속을 통해 애초에 없애는 패턴입니다.
State pattern 을 적용해서 Context 클래스를 바꿔보면 다음처럼 됩니다.
{
State * pState;
Request()
{
pState->Handle();
}
GetValue()
{
}
}
분기문이 보이지 않네요. State 클래스들도 추가해 보지요.
{
Handle();
GetValue();
}; class InitiatingState : State
{
Handle()
{
// Do some initiating works
}
GetValue()
{ return 0; }
};
class RunningState : State
{
Handle()
{
// Do some works for running mode
}
GetValue()
{ return 1; }
};
class IdlingState : State
{
Handle()
{
// Do some works for Idling mode
}
GetValue()
{ return 2; }
};
이로써 if-else 구문은 없어지고 if else 의 각 분기에 해당하는 코드들은 State 클래스를 상속받아 구현한 ConcreteState Class 들 즉, InitiatingState, RunningState, IdlingState 로 나눠졌습니다. 물론 여기서 Context 에 있는 State 포인터 값을 바꿔주는 코드가 따로 필요 합니다.
{
Context ct;
ct.pState = new InitiatingState();
ct.Request();
ct.pState
= new RunningState();print ct.GetValue();
ct.pState
= new IdlingState();ct.Request();
}
Context 를 쓰는 클라이언트 코드는 위처럼 되겠지요. 이렇게 하면 새로운 State 가 추가되거나 기존의 State 에 따른 코드가 바뀌어야 할 때, 소스코드 전체에 퍼져 있는 if-else 를 찾아 바꿔줘야 하는 수고를 덜 수 있습니다. 해당 ConcreteState 코드만 바꿔주거나 추가하면 충분합니다.
하지만, 위 코드도 약간 개선할 점들이 있습니다. 보시다시피 State 가 바뀔때마다 새로운 ConcreteState 인스턴스를 만들어 할당해줘야 되기 때문에, 불필요한 메모리 할당이 자주 일어날 수 있습니다. 이럴 경우 Singleton pattern 이나 Flyweight pattern 을 활용해 보면 되겠지요.