객체의 상태에 따라 다른 행동을 할 때, 상태 패턴(State Pattern)을 활용해 효율적으로 행동을 구현할 수 있다.
컴퓨터 프로그램의 설계에서 자주 쓰이는 개념 중 Finite State Machine(유한 상태 기계)이라는 개념이 있다. Machine은 한 번에 하나의 상태를 가지고, 어떤 이벤트를 통해 다른 상태로 전환될 수 있다.
게임 캐릭터나 몬스터의 상태, 애니메이션에서 이런 설계는 자주 볼 수 있다.
상태에 따라 다른 행동을 하는 객체를 조건문으로 구현할 수도 있으나 이러한 코드는 유지 보수가 매우 어렵다.
switch (state)
"draft":
state = "moderation"
break
"moderation":
if (currentUser.role == "admin")
state = "published"
break
"published":
// Do nothing.
break
구현
상태 패턴은
- Context
- State
- Concrete State
로 구성된다.
Context는 클라이언트가 객체의 상태 변화를 요청하는 인터페이스를 정의하고 현재 상태를 저장한다.
State는 인터페이스로 Concrete State와 연결한다.
Concrete State는 상태별 메소드를 구현한 클래스이다.
Context
class Context
{
private:
State* currentState;
public:
Context()
{
// 초기 상태는 꺼진 상태
currentState = new OffState();
}
void setState(State* state)
{
currentState = state;
}
void turnOn()
{
currentState->turnOn();
setState(new OnState()); // 켤 때 상태를 On으로 변경
}
void turnOff()
{
currentState->turnOff();
setState(new OffState());// 끌 때 상태를 Off로 변경
}
};
State
class State
{
public:
virtual void turnOn() = 0;
virtual void turnOff() = 0;
};
Concrete State
// 켜는 상태를 나타내는 ConcreteState
class OnState : public State
{
public:
void turnOn() override
{
std::cout << "이미 켜져 있습니다." << std::endl;
}
void turnOff() override
{
std::cout << "끄기" << std::endl;
}
};
// 끄는 상태를 나타내는 ConcreteState
class OffState : public State
{
public:
void turnOn() override
{
std::cout << "켜기" << std::endl;
}
void turnOff() override
{
std::cout << "이미 꺼져 있습니다." << std::endl;
}
};
예시
int main()
{
Context bulb; // 전구를 켜고 끄는 행동을 시뮬레이션
bulb.turnOn();
bulb.turnOff();
bulb.turnOn();
bulb.turnOn();
bulb.turnOff();
return 0;
}
장점
- 단일 책임 원칙을 지킨다.
- 개방/폐쇄 원칙을 지킨다.
단점
- 상태가 몇 가지밖에 없다면 상태 패턴을 적용하는 것이 과도할 수도 있다.
반응형