What is a state pattern?
State Pattern
is one of behavioral design patterns that allows an object to change its behavior when there are internal state changes.
This design pattern can be understood almost like Strategy
, which can transform strategies through the methods defined in the interface.
Problem
Consider a TCPConnection
class that represents a network connection. The TCPConnection
connection TCPConnection
can be in one of several different states: Established
, Listening
, Closed
. When a TCPConnection
object receives requests from other objects, it will perform different behaviors depending on its current state.
For example, the outcome of an Open request depends on whether the connection is Closed
or Established
. State pattern describes how TCPConnection
differently depending on each state
The main idea in this pattern is to introduce an abstract class called TCPState
to represent the state of network connectivity. The TCPState
class TCPState
a common interface for all classes that show different states of activity. TCPState
will implement specific behaviors.
For example, the TCPEstablished
and TCPClosed
classes will implement specific actions for the Established and Closed states of TCPConnection.
Application of State Pattern
We will apply the State Pattern in the following cases:
- The behavior of a statue depends on its status. At runtime, when the object performs behavior, its state will change accordingly.
- The object has many use cases with its behaviors, many of which depend on the state of the object. In other words, the object has many states, each of which has different behaviors.
Structure of State Pattern
Main ingredient
Context
- Define the main interface to communicate with clients.
- Contains an instance of
ConcreateState
corresponding to the current state of the object.
State
- Interface definition to encapsulate the behavior of communicating with each state of the
Context
ConcreateState subclasses
- Each subclass implements a behavior that communicates with a state of the
Context
Activity stream
Context
will define behaviors that can communicate with Clients, so clients will request behaviors through Context.Context
will contain an instance of theState
, theState
initially be installed from the client, but once installed, the clients cannot modify it.Context
can send itself as an argument to theState
, so theState
can access theContext
to change the status if needed.- When the
Context
performs the action, it will call the currentState
to perform the behavior, done, theState
may change the status from the Context if needed.
Result
Zoning the behavior of objects with different states.
The object is explicitly changed state.
The state of objects that can be shared.
Example
We will define a State
interface and have its own two states: LowerCaseState
and MultipleUpperCaseState
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | <span class="token keyword">interface</span> <span class="token class-name">State</span> <span class="token punctuation">{</span> <span class="token keyword">void</span> <span class="token function">writeName</span> <span class="token punctuation">(</span> StateContext context <span class="token punctuation">,</span> String name <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">class</span> <span class="token class-name">LowerCaseState</span> <span class="token keyword">implements</span> <span class="token class-name">State</span> <span class="token punctuation">{</span> <span class="token annotation punctuation">@Override</span> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">writeName</span> <span class="token punctuation">(</span> StateContext context <span class="token punctuation">,</span> String name <span class="token punctuation">)</span> <span class="token punctuation">{</span> System <span class="token punctuation">.</span> out <span class="token punctuation">.</span> <span class="token function">println</span> <span class="token punctuation">(</span> name <span class="token punctuation">.</span> <span class="token function">toLowerCase</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> context <span class="token punctuation">.</span> <span class="token function">setState</span> <span class="token punctuation">(</span> <span class="token keyword">new</span> <span class="token class-name">MultipleUpperCaseState</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">class</span> <span class="token class-name">MultipleUpperCaseState</span> <span class="token keyword">implements</span> <span class="token class-name">State</span> <span class="token punctuation">{</span> <span class="token comment">/* Counter local to this state */</span> <span class="token keyword">private</span> <span class="token keyword">int</span> count <span class="token operator">=</span> <span class="token number">0</span> <span class="token punctuation">;</span> <span class="token annotation punctuation">@Override</span> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">writeName</span> <span class="token punctuation">(</span> StateContext context <span class="token punctuation">,</span> String name <span class="token punctuation">)</span> <span class="token punctuation">{</span> System <span class="token punctuation">.</span> out <span class="token punctuation">.</span> <span class="token function">println</span> <span class="token punctuation">(</span> name <span class="token punctuation">.</span> <span class="token function">toUpperCase</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token comment">/* Change state after StateMultipleUpperCase's writeName() gets invoked twice */</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> <span class="token operator">++</span> count <span class="token operator">></span> <span class="token number">1</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> context <span class="token punctuation">.</span> <span class="token function">setState</span> <span class="token punctuation">(</span> <span class="token keyword">new</span> <span class="token class-name">LowerCaseState</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
The Context
class will contain a state variable, typically the current state of the Context
, in the constructor, the state
will be assigned a default state.
In addition, the Context
class will define a setter function for the state
to change the state every time it executes behavior
The behavior performed here is writeName
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | <span class="token keyword">class</span> <span class="token class-name">StateContext</span> <span class="token punctuation">{</span> <span class="token keyword">private</span> State state <span class="token punctuation">;</span> <span class="token keyword">public</span> <span class="token function">StateContext</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> state <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">LowerCaseState</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">/** * Set the current state. * Normally only called by classes implementing the State interface. * @param newState the new state of this context */</span> <span class="token keyword">void</span> <span class="token function">setState</span> <span class="token punctuation">(</span> State newState <span class="token punctuation">)</span> <span class="token punctuation">{</span> state <span class="token operator">=</span> newState <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">writeName</span> <span class="token punctuation">(</span> String name <span class="token punctuation">)</span> <span class="token punctuation">{</span> state <span class="token punctuation">.</span> <span class="token function">writeName</span> <span class="token punctuation">(</span> <span class="token keyword">this</span> <span class="token punctuation">,</span> name <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Then, in main (client) function, we will initialize the Context and execute their behavior:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">StateDemo</span> <span class="token punctuation">{</span> <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">main</span> <span class="token punctuation">(</span> String <span class="token punctuation">[</span> <span class="token punctuation">]</span> args <span class="token punctuation">)</span> <span class="token punctuation">{</span> StateContext context <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">StateContext</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> context <span class="token punctuation">.</span> <span class="token function">writeName</span> <span class="token punctuation">(</span> <span class="token string">"Monday"</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> context <span class="token punctuation">.</span> <span class="token function">writeName</span> <span class="token punctuation">(</span> <span class="token string">"Tuesday"</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> context <span class="token punctuation">.</span> <span class="token function">writeName</span> <span class="token punctuation">(</span> <span class="token string">"Wednesday"</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> context <span class="token punctuation">.</span> <span class="token function">writeName</span> <span class="token punctuation">(</span> <span class="token string">"Thursday"</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> context <span class="token punctuation">.</span> <span class="token function">writeName</span> <span class="token punctuation">(</span> <span class="token string">"Friday"</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> context <span class="token punctuation">.</span> <span class="token function">writeName</span> <span class="token punctuation">(</span> <span class="token string">"Saturday"</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> context <span class="token punctuation">.</span> <span class="token function">writeName</span> <span class="token punctuation">(</span> <span class="token string">"Sunday"</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
The result will be printed as follows:
1 2 3 4 5 6 7 8 | monday TUESDAY WEDNESDAY thursday FRIDAY SATURDAY sunday |
As you can see, every time we perform the writeName function, the state is converted once, resulting in our output being different each time.
Related Design Patterns
If above, one of the results of this design pattern is that states of objects can be shared, the Flyweight design will indicate when to share states and how to perform them. will be
Normally, State objects are designed according to the Singleton pattern, because states themselves only need a single instance.
Refer
[1] Erich Gamma, Richard Helm, Ralph Johnson, John M. Vlissides (1995). Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley. ISBN 0-201-63361-2.
[2] http://www.w3sdesign.com/GoF_Design_Patterns_Reference0100.pdf