A table-driven approach to designing finite state machines does a good job of specifying state transitions, but it is difficult to add actions to accompany the state transitions. The pattern-based approach uses code (instead of data structures) to specify state transitions, but it does a good job of accomodating state transition actions. [GoF, p308]
The state machine's interface is encapsulated in the "wrapper" class. The wrappee hierarchy's interface mirrors the wrapper's interface with the exception of one additional parameter. The extra parameter allows wrappee derived classes to call back to the wrapper class as necessary. Complexity that would otherwise drag down the wrapper class is neatly compartmented and encapsulated in a polymorphic hierarchy to which the wrapper object delegates.
this
pointer is passed.
Before | After | |
---|---|---|
// 3-speed ceiling fan state machine // Not good: unwieldy "case" statement class CeilingFanPullChain { private int m_current_state; public CeilingFanPullChain() { m_current_state = 0; } public void pull() { if (m_current_state == 0) { m_current_state = 1; System.out.println( " low speed" ); } else if (m_current_state == 1) { m_current_state = 2; System.out.println( " medium speed" ); } else if (m_current_state == 2) { m_current_state = 3; System.out.println( " high speed" ); } else { m_current_state = 0; System.out.println( " turning off" ); } } } public class StateDemo { public static void main( String[] args ) { CeilingFanPullChain chain = new CeilingFanPullChain(); while (true) { System.out.print( "Press | // The CeilingFanPullChain class is now a wrapper // that delegates to its m_current_state reference. // Each clause from the "before" case statement is // now captured in a State derived class. // For this simple domain, the State pattern is // probably over-kill. class CeilingFanPullChain { private State m_current_state; public CeilingFanPullChain() { m_current_state = new Off(); } public void set_state( State s ) { m_current_state = s; } public void pull() { m_current_state.pull( this ); } } interface State { void pull( CeilingFanPullChain wrapper ); } class Off implements State { public void pull( CeilingFanPullChain wrapper ) { wrapper.set_state( new Low() ); System.out.println( " low speed" ); } } class Low implements State { public void pull( CeilingFanPullChain wrapper ) { wrapper.set_state( new Medium() ); System.out.println( " medium speed" ); } } class Medium implements State { public void pull( CeilingFanPullChain wrapper ) { wrapper.set_state( new High() ); System.out.println( " high speed" ); } } class High implements State { public void pull( CeilingFanPullChain wrapper ) { wrapper.set_state( new Off() ); System.out.println( " turning off" ); } } public class StateDemo { public static void main( String[] args ) { CeilingFanPullChain chain = new CeilingFanPullChain(); while (true) { System.out.print( "Press |
Flyweight explains when and how State objects can be shared. [GoF, p313]
Interpreter can use State to define parsing contexts. [GoF, p349]
State is like Strategy except in its intent. [Coplien, C++ Report, Mar 96, p88]
Strategy has 2 different implementations, the first is similar to State. The difference is in binding times (Strategy is a bind-once pattern, whereas State is more dynamic). [Coplien, C++ Report, Mar 96, p88]
State, Strategy, Bridge (and to some degree Adapter) have similar solution structures. They all share elements of the "handle/body" idiom [Coplien, Advanced C++, p58; see discussion under Bridge pattern]. They differ in intent - that is, they solve different problems.
The structure of State and Bridge are identical (except that Bridge admits hierarchies of envelope classes, whereas State allows only one). The two patterns use the same structure to solve different problems: State allows an object's behavior to change along with its state, while Bridge's intent is to decouple an abstraction from its implementation so that the two can vary independently. [Coplien, C++ Report, May 95, p58]
The implementation of the State pattern builds on the Strategy pattern. The difference between State and Strategy is in the intent. With Strategy, the choice of algorithm is fairly stable. With State, a change in the state of the "context" object causes it to select from its "palette" of Strategy objects. [Coplien, Multi-Paradigm Design for C++, Addison-Wesley, 1999, p253]