STATE
Object Behavioral Pattern

Intent | Motivation | Applicability | Structure | Participants | Collaborations
Consequences | Implementation | Sample Code | Known Uses | Related Patterns


Intent

Allow an object to alter its behavior when its internal state changes. The object will appear to change its class.

Motivation

Abstract representations for changes in state are to be created. For example, consider a class TCPConnection that represents different states of a network connection. The effect of a request such as Open depends on the current state of the connection.

By creating an abstract TCPState class, subclasses can define the state-specific behavior.

Applicability

Use the state pattern in either of the following cases:

Structure

State
Pattern

Participants

Collaborations

Consequences

  1. It localizes state-specific behavior and partitions behavior for different states.
  2. It makes state transitions explicit.
  3. State objects can be shared.

Implementation

  1. Who defines the state transitions?
  2. A table-based alternative.
  3. Creating and destroying State objects.
  4. Using dynamic inheritance.

Sample Code

Consider a C++ model for a TCP connection. Start by defining the TCPConnection class, which will provide an interface for transmitting data and hadles requests to change state.

class TCPOctetStream;
class TCPState;

class TCPConnection {
public:
    TCPConnection();
    void ActiveOpen();
    void PassiveOpen();
    void Close();
    void Send();
    void Acknowledge();
    void Synchronize();
    void ProcessOctet( TCPOctetStream* )
private:
    friend class TCPState;
    void ChangeState( TCPState* );
private:
    TCPState* _state;
};

TCPConnection keeps an instance of the TCPState class in the _state member variable. The class TCPState duplicates the state-changing interface of TCPConnection.

class TCPState {
public:
    virtual void Transmit( TCPConnection*, TCPOctetStream* );
    virtual void ActiveOpen( TCPConnection* );
    virtual void PassiveOpen( TCPConnection* );
    virtual void Close( TCPConnection* );
    virtual void Synchronize( TCPConnection* );
    virtual void Acknowledge( TCPConnection* );
    virtual void Send( TCPConnection* );
protected:
    void ChangeState( TCPConnection*, TCPState* );
};

TCPConnection delegates all state-specific requests to its TCPState instance. It also provides an operating for changing this variable to a new TCPState. The constructor initializes the object to the TCPClosed state.

TCPConnection::TCPConnection()
{ _state = TCPClosed::Instance(); }

void TCPConnection::ChangeState( TCPState *s )
{ _state = s; }

void TCPConnection::ActiveOpen()
{ _state->ActiveOpen(this); }

void TCPConnection::PassiveOpen()
{ _state->PassiveOpen(this); }

void TCPConnection::Close()
{ _state->Close(this); }

void TCPConnection::Acknowledge()
{ _state->Acknowledge(this); }

void TCPConnection::Synchronize()
{ _state->Synchronize(this); }

TCPState implements default behavior for all requests delegated to it. It can change the state of a TCPConnection with ChangeState(). It is declared a friend of TCPConnection to give it privilaged access to this operation.

void TCPState::Transmit( TCPConnection*, TCPOctetStream* ) {}
void TCPState::ActiveOpen( TCPConnection* ) {}
void TCPState::PassiveOpen( TCPConnection* ) {}
void TCPState::Close( TCPConnection* ) {}
void TCPState::Synchronize( TCPConnection* ) {}

void TCPState::ChangeState( TCPPConnection *t, TCPState *s )
{ t->ChangeState(s); }

Subclasses of TCPState implement state-specific behavior. A TCP connection can be in many states: Established, Listening, Closed, etc., and there's a subclass of TCPState for each state. Consider those three states.

class TCPEstablished : public TCPState {
public:
    static TCPState* Instance();
    virtual void Transmit( TCPConnection*, TCPOctetStream* );
    virtual void Close( TCPConnection* );
};

class TCPListen : public TCPState {
public:
    static TCPState* Instance();
    virtual void Send( TCPConnection* );
    // ...
};

class TCPClosed : public TCPState {
public:
    static TCPState* Instance();
    virtual void ActiveOpen( TCPConnection* );
    virtual void PassiveOpen( TCPConnection* );
    // ...
};

The subclasses maintain no local state, so they can be shared and only one instance of each is required. The unique instance of each TCPState subclass is obtained by the static Instance() operation, thus making the class a Singleton.

Each subclass implements state-specific behavior for valid requests in the state:

void TCPClosed::ActiveOpen( TCPConnection *t ) {
    // send SYN, receive SYN, ACK, etc.
    ChangeState( t, TCPEstablished::Instance() );
}

void TCPClosed::PassiveOpen( TCPConnection *t ) {
    ChangeState( t, TCPListen::Instance() );
}

void TCPEstablished::Close( TCPConnection *t ) {
    // send FIN, receive ACK of FIN
    ChangeState( t, TCPListen::Instance() );
}

void TCPEstablished::Transmit( TCPConnection *t, TCPOctetStream *o ) {
    t->ProcessOctet( o );
}

void TCPListen::Send( TCPConnection *t ) {
    // send SYN, receive SYN, ACK, etc.
    ChangeState( t, TCPEstablished::Instance() );
}

After performing state-specific work, these operations call the ChangeState() operation to change the state of the TCPConnection. The TCPConnection itself doesn't know a thing about the TCP connection protocol, it's the TCPState subclasses that define each state transition and action in TCP.

Known Uses

Consider the pressing of buttons on the CAVE wand. Currently, this is done by using if statements to check for CAVEBUTTON1, CAVEBUTTON2, and CAVEBUTTON3. Implementing these into a State pattern allows for extendibility should other devices be used the future.

Related Patterns

The Flyweight pattern explains when and how State objects can be shared. State objects are often Singletons.


Sourced from Design Patterns: Elements of Reusable Object-Oriented Software, by Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides, and Grady Booch, Addison-Wesley Publishing Company, 1994.