state pattern

예전에 C로 공장 자동화 프로그래밍을 할 때나, 한글 오토마타 프로그래밍을 할 때, switch - case 문으로

도배하여 프로그래밍을 했다.

 

가령 이런 코드 방식이다.

int m_state = 0;

 

 

void Process

{

    switch( m_state )

    {

    case 0:

        if( CheckWeight() )

            m_state = 1;

        break;

    case 1:

        if( FillWater() )

            m_state = 2;

        break;

    case 2:

        if( MixTank() )

            m_state = 3;

        break;

    case 3:

        Alarm();

        break;

    }

}

 

코드를 간단하게 짰기 때문에, 복잡해 보이지 않는다. 실제로 프로그래밍 하게 된다면, state를 이리저리

오가는 코드로 인하여, 코드는 지저분하게 된다.

 

이것을 해결하기 위한 패턴이 state 패턴이다. switch - case 문을 state 패턴으로 바꾸는 방법은 case를

하나의 state 클래스로 바꾸면 된다.

 

 

위의 UML 처럼 case 문을 Concreate StateA, Concreate StateB 처럼 변환 하면 된다.

 

Context::ChangeState()문을 통해 State를 바꾼다.
ChangeState문을 State 클래스 내부에서 호출하는 방식과 외부에서 호출하는 방식이 있다.
내부에서 호출 하는 방식이 내가 보기에는 깔끔해 보여서 내부에서 바꾸는 방식으로 했다.

 

State::Execute() 메쏘드는 업데이트 마다 호출 되는 메쏘드로 내부에서 조건문에 따라
ChangeState()를 실행해 준다.

 

//actor.cpp

#include <stdio.h>

#include <iostream>

 

//actor 상태를 state 패턴으로 구현해 본다.

 

class IState;

class ActorStandState;

class ActorJumpState;

class ActorFallState;

 

//--------------------------------------------------------

class IContext

{

public:

    IContext() {  m_pState = NULL;   }

    virtual ~IContext();

    virtual void  ChangeState( IState* pNextState );

    virtual void  SetAnimation( const char* szAnimation ) = 0;

    virtual float GetJumpHeight() = 0;  

 

protected:

    IState* m_pState;

};

 

 

class IState

{

public:

    virtual void Enter( IContext* pContext ) = 0;

    virtual void Exit( IContext* pContext ) = 0;

    virtual void Execute( IContext* pContext ) = 0;

};

//--------------------------------------------------------

IContext::~IContext()

{

    if( m_pState )

    {

        delete m_pState;

        m_pState = NULL;

    }

}

//--------------------------------------------------------

void IContext::ChangeState( IState* pNextState )

{

    if( m_pState )

    {

        m_pState->Exit( this );

        delete m_pState;

    }

 

    m_pState = pNextState;

    m_pState->Enter( this );

}

 

class ActorStandState : public IState

{

public:

    void Enter( IContext* pContext )

    {

        pContext->SetAnimation( "stand" );

    }

    void Exit( IContext* pContext )

    {

 

    }

    void Execute( IContext* pContext );

};

//--------------------------------------------------------

class ActorJumpState : public IState

{

public:

    void Enter( IContext* pContext )

    {

        pContext->SetAnimation( "jump" );

    }

    void Exit( IContext* pContext )

    {

 

    }

    void Execute( IContext* pContext );

};

//--------------------------------------------------------

class ActorFallState : public IState

{

public:

    void Enter( IContext* pContext )

    {

        pContext->SetAnimation( "fall" );

    }

    void Exit( IContext* pContext )

    {

 

    }

    void Execute( IContext* pContext );

};

 

//--------------------------------------------------------

void ActorStandState::Execute( IContext* pContext )

{

    std::cout << "ActorStandState" << std::endl;

 

    if( pContext->GetJumpHeight() >= 0.1f )    //점프 시작

        pContext->ChangeState( new ActorJumpState );

}

//--------------------------------------------------------

void ActorJumpState::Execute( IContext* pContext )

{

    std::cout << "ActorJumpState" << std::endl;

 

    if( pContext->GetJumpHeight() >= 1.0f )   //actor는 최고 1미터까지 점프한다.

        pContext->ChangeState( new ActorFallState );

}

//--------------------------------------------------------

void ActorFallState::Execute( IContext* pContext )

{

    std::cout << "ActorFallState" << std::endl;

 

    if( pContext->GetJumpHeight() <= 0.0f )

        pContext->ChangeState( new ActorStandState );

}

//--------------------------------------------------------

class Actor : public IContext

{

public:

    Actor()

    {

        m_height = 0.0f;

    }

 

    void  SetAnimation( const char* szAnimation )   {};

    float GetJumpHeight()  { return m_height; }

    void  SetJumpHiehgt( float height ) { m_height = height; }

    void  Update()

    {

        m_pState->Execute( this );

    }

    float    m_height;

};

 

 

void main()

{

    Actor actor;

    actor.ChangeState( new ActorStandState );

 

    float height = 0.0f;

    for( ; height <= 1.0f; height += 0.3f )

    {

        actor.SetJumpHiehgt( height );

        actor.Update();

    }

 

    for( ; height >= 0.0f; height -= 0.3f )

    {

        actor.SetJumpHiehgt( height );

        actor.Update();

    }

 

    actor.Update();

}

 

위의 코드 예는 캐릭터가 점프 할 때의 상태를 상태 패턴으로 만든 것이다.

패턴은 다음과 같다.

 

캐릭터가 지면위에 서 있을 때 패턴

점프하여 올라가는 중일때,

점프후 내려 올때,

 

이 들 상태는 지면과 높이에 따라, 상태가 정해 진다.

 

소스:  state_pattern.cpp

 

참조 싸이트:

http://www.gotoandplay.it/_articles/2004/08/statePattern_p02.php

http://gpwiki.org/index.php/State_pattern

http://blogs.microsoft.co.il/blogs/gilf/archive/2008/08/02/state-pattern.aspx