Intent |
Motivation |
Applicability |
Structure |
Participants |
Collaborations
Consequences |
Implementation |
Sample Code |
Known Uses |
Related Patterns
Allow an object to alter its behavior when its internal state changes. The object will appear to change its class.
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.
Use the state pattern in either of the following cases:
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.
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.
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.