Avijeet Dash, Satyabrata
Dash and Bhaskar V
Intent
Allow an object to alter its behavior when its internal State changes. The
object will appear to change its class.
In 30 Seconds
The catch here is ‘implementing discrete object states using explicit
classes’. Many a times we deal with objects, which have a number of discrete
states and the object’s behavior depends on the flow of State transitions. For
example: an object like TCPConnection would go from states like Established,
Listen to Closed. We have two options to implement this. One, we keep all these
states as constant identifiers and clutter the code with conditional logic to
keep checking the states, doing the State change and the respective action for a
particular State. Second approach is to define different State classes, this is
what State Pattern stands for and this adds perfume to the code if long
conditional code in one class stinks.
Motivation
From an object collaboration angle, two kinds of objects come into picture.
One is the ‘Context’ and other is the ‘State’. Context is the object,
which goes through all different states. The Pattern suggests having a composite
relationship between the Context and State. Let the Context keep an object
reference to the State. All different states are Concrete state classes
inheriting from an abstract State class. The State classes implement the
handle() method which localizes the actions to be taken on an event in the State
machine. The Client configures the Context with the initial State and then the
States take care of the State transition as well as the action to be performed.
For example: TCPConnection will have TCPEstablished, TCPListen and TCPClosed
State classes. TCPEstablised will implement the handle() method where it will
dictate the things to do on a connection being established and the State
transition after that.
State Pattern has many benefits: it makes State transition explicit,
localizes the State specific behavior and partitions behavior for different
states.
Sample Code
State.java
abstract class State { public abstract void setCurrentState(Context c); public abstract void handle(); public abstract void prt(); } ConcreteStateA.java — implemented as Singleton
class ConcreteStateA extends State { private static ConcreteStateA _currentState = null; public static State Instance() { if (_currentState==null) { _currentState = new ConcreteStateA();
} return(_currentState); } public void setCurrentState(Context c) { c.changeState(_currentState); } public void handle() { this.prt(); } public void prt() { System.out.println("Current state is ConcreteStateA."); } }
Context.java
class Context { public Context() { _state = ConcreteStateA.Instance(); } public void request() { _state.handle(); } public void changeState( State s) { _state = s; } private State _state; }
Client Code Context context = new Context(); State concreteConcreteStateA = ConcreteStateA.Instance(); context.request(); // State having a handle to Context concreteConcreteStateA.setCurrentState(context); context.request();
Complexities simplified
Who defines the State transitions
This can be done either by the State or the Context. Moving the State
transition logic to State classes make the State classes aware of each other,
which kind of make them dependent. On the other hand this decentralization helps
extend the system with more states and easy to modify the Context.
State Pattern — implemented as a look up table
This is a different approach to structure the State-driven code. For each
State, a table maps every possible input to a succeeding State. In effect, this
approach converts the conditional code to a table look up. This has its own pros
and cons. Basically, this approach models the State transitions rather than the
State specific behavior.
State Pattern — dynamic inheritance
The ability to change the State at run time, in a sense is like changing
classes (inheritance structure) at run time. This can be viewed as dynamic
inheritance, which is achieved using State Pattern.
Also Known as and Related Patterns
State objects can be used as flyweight if they don’t have any instance
variables representing its intrinsic State.