Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?
Autor: Piotr Szawdyński
Wzorce projektowe

stan

[wzorzec projektowy] Automatycznie kontroluje wewnętrzne zachowanie obiektu, nadając mu odpowiedni stan i funkcjonalność.

Budowa wzorca projektowego

C/C++
class IState
{
public:
    virtual void metoda1() = 0;
    virtual void metoda2( int argument ) = 0;
    virtual int metoda3( double argument ) = 0;
};

class CObject_State1
    : public IState
{
    //pełna implementacja interfejsu
};

class CObject_State2
    : public IState
{
    //pełna implementacja interfejsu
};

class CObject_State3
    : public IState
{
    //pełna implementacja interfejsu
};

class CObject
{
private:
    IState * m_pState;
public:
    void uruchom_metode1();
    void uruchom_metode2( int argument );
    int uruchom_metode3( double argument );
};

Opis szczegółowy

Stan (ang. state) - wzorzec projektowy, który ma na celu ułatwić rozbudowę obiektu o nowe stany. Wraz ze zmianą stanu następuje zmiana zachowania obiektu.

Wszystkie stany dla danego obiektu muszą posiadać wspólny interfejs. Stany są obiektami tymczasowymi. Dane, które mają być trwałe muszą zostać zapisane do obiektu głównego. Dane tymczasowe powinny być trzymane w stanie do czasu uzyskania kompletnego zestawu danych, a następnie przekazane do obiektu głównego.

Dodatkowe informacje

» Wzorce projektowe » Wzorce czynnościowestrategia wzorzec projektowy jest wzorcem projektowym o takiej samej konstrukcji jak state. Istnieje jednak między nimi istotna różnica w zasadzie działania. Wzorzec projektowy state powinien posiadać następujące własności:
  • stan obiektu może ulec zmianie w wyniku wykonania jakiegoś zadania bądź upływu określonej ilości czasu;
  • stan obiektu nie powinien być ustawiany spoza klasy - ewentualne zmiany stanów powinny odbywać się za pomocą metod, które dbają o prawidłową modyfikację stanu obiektu.

Przykład - szkielet klasy głównej

C/C++
class CObject
{
private:
    IState * m_pState;
    //...
public:
    void selectPen()
    {
        m_pState = new CPen( this );
    }
   
    void selectLine()
    {
        m_pState = new CLine( this );
    }
   
    void selectRect()
    {
        m_pState = new CRect( this );
    }
   
    void onMouseDown( const CPoint & pos )
    {
        if( m_pState )
             m_pState->onMouseDown( pos );
       
    }
   
    void onMouseUp( const CPoint & pos )
    {
        if( m_pState )
             m_pState->onMouseUp( pos );
       
    }
   
    void onMouseMove( const CPoint & pos )
    {
        if( m_pState )
             m_pState->onMouseMove( pos );
       
    }
}; //class CObject

Przykład - kompletna aplikacja

C/C++
#include <cstdio>
#include <cctype>
#include <sstream>
#include <list>
#include <algorithm>

class CTelevisor;

class TTelevisor_State
{
private:
    CTelevisor * const m_pTv;
protected:
    CTelevisor * const getParent();
public:
    TTelevisor_State( CTelevisor * const pTv );
    virtual ~TTelevisor_State();
   
    virtual void putInput( char c ) = 0;
}; //class TTelevisor_State

class CTelevisor
{
private:
    friend class TTelevisor_State;
    void setState( TTelevisor_State * pState );
    TTelevisor_State * m_pState;
    typedef std::list < TTelevisor_State *> StatesT;
    StatesT m_toDelete;
    void eraseOldStates();
    static void eraseState( TTelevisor_State * pState );
protected:
    //INFO: tu uproszczenie (friendy) po to by nie dodawać metod do zarządzania poniższymi zmiennymi
    friend class CTVState_Idle;
    friend class CTVState_PowerOnOff;
    friend class CTVState_PickChannel;
   
    bool m_bIsTurnedOn;
    int m_iChannel;
public:
    CTelevisor();
    ~CTelevisor();
    void recvSignal( char c );
    void powerOn();
    void powerOff();
}; //class CTelevisor

class CTVState_PowerOnOff
    : public TTelevisor_State
{
public:
    CTVState_PowerOnOff( CTelevisor * const pTv );
    virtual void putInput( char c );
}; //class CTVState_PowerOnOff

class CTVState_Idle
    : public TTelevisor_State
{
public:
    CTVState_Idle( CTelevisor * const pTv );
    virtual void putInput( char c );
}; //class CTVState_Idle

class CTVState_PickChannel
    : public TTelevisor_State
{
private:
    std::stringstream m_sNewChannel;
public:
    CTVState_PickChannel( CTelevisor * const pTv );
    virtual void putInput( char c );
}; //class CTVState_PickChannel

int main()
{
    CTelevisor tv;
    tv.powerOn();
    tv.recvSignal( '1' );
    tv.recvSignal( '0' );
    tv.powerOff();
    tv.powerOn();
    tv.recvSignal( '0' );
    tv.recvSignal( '7' );
    tv.powerOff();
    return 0;
}

CTelevisor * const TTelevisor_State::getParent()
{
    return m_pTv;
}

TTelevisor_State::TTelevisor_State( CTelevisor * const pTv )
    : m_pTv( pTv )
{
}

//virtual
TTelevisor_State::~TTelevisor_State()
{
}

void CTelevisor::setState( TTelevisor_State * pState )
{
    if( m_pState )
         m_toDelete.push_back( m_pState );
   
    m_pState = pState;
}

CTelevisor::CTelevisor()
    : m_pState( NULL )
    , m_bIsTurnedOn( false )
    , m_iChannel( 1 )
{
}

CTelevisor::~CTelevisor()
{
    eraseOldStates();
}

void CTelevisor::recvSignal( char c )
{
    if( m_pState )
         m_pState->putInput( c );
   
    eraseOldStates();
}

void CTelevisor::powerOn()
{
    printf( "powerOn()\n" );
    CTVState_PowerOnOff * pPower = new CTVState_PowerOnOff( this );
    setState( pPower );
    pPower->putInput( '*' );
    eraseOldStates();
}

void CTelevisor::powerOff()
{
    printf( "powerOff()\n" );
    CTVState_PowerOnOff * pPower = new CTVState_PowerOnOff( this );
    setState( pPower );
    pPower->putInput( '*' );
    eraseOldStates();
}

void CTelevisor::eraseOldStates()
{
    std::for_each( m_toDelete.begin(), m_toDelete.end(), eraseState );
    m_toDelete.clear();
}

//static
void CTelevisor::eraseState( TTelevisor_State * pState )
{
    delete pState;
}

CTVState_PowerOnOff::CTVState_PowerOnOff( CTelevisor * const pTv )
    : TTelevisor_State( pTv )
{
    printf( "[constructed] CTVState_PowerOnOff()\n" );
}

//virtual
void CTVState_PowerOnOff::putInput( char c )
{
    if( c != '*' )
         return;
   
    getParent()->m_bIsTurnedOn = !getParent()->m_bIsTurnedOn;
    if( getParent()->m_bIsTurnedOn )
         getParent()->setState( new CTVState_Idle( getParent() ) );
    else
         getParent()->setState( NULL );
   
}

CTVState_Idle::CTVState_Idle( CTelevisor * const pTv )
    : TTelevisor_State( pTv )
{
    printf( "[constructed] CTVState_Idle()\n" );
    printf( "\tTV is idling at channel %d\n", getParent()->m_iChannel );
}

//virtual
void CTVState_Idle::putInput( char c )
{
    if( !::isdigit( c ) )
         return;
   
    CTVState_PickChannel * pChannel = new CTVState_PickChannel( getParent() );
    getParent()->setState( pChannel );
    pChannel->putInput( c );
}

CTVState_PickChannel::CTVState_PickChannel( CTelevisor * const pTv )
    : TTelevisor_State( pTv )
{
    printf( "[constructed] CTVState_PickChannel()\n" );
}

//virtual
void CTVState_PickChannel::putInput( char c )
{
    if( !::isdigit( c ) )
         return;
   
    m_sNewChannel << c;
    if( m_sNewChannel.str().length() != 2 )
         return;
   
    m_sNewChannel >> getParent()->m_iChannel;
    getParent()->setState( new CTVState_Idle( getParent() ) );
}
Standardowe wyjście programu:
powerOn()
[constructed] CTVState_PowerOnOff()
[constructed] CTVState_Idle()
        TV is idling at channel 1
[constructed] CTVState_PickChannel()
[constructed] CTVState_Idle()
        TV is idling at channel 10
powerOff()
[constructed] CTVState_PowerOnOff()
powerOn()
[constructed] CTVState_PowerOnOff()
[constructed] CTVState_Idle()
        TV is idling at channel 10
[constructed] CTVState_PickChannel()
[constructed] CTVState_Idle()
        TV is idling at channel 7
powerOff()
[constructed] CTVState_PowerOnOff()