Easy patterns: State

Ruslan Malogulko
ITNEXT
Published in
4 min readApr 15, 2019

--

Structure of the State pattern

This article is created in continuation of easy patterns series description and presents a behavioral pattern named a State which helps to the object to alter its behavior when its internal state changes.

Please refer to the other pattern articles as well:

Creational patterns:

Simple Factory

Factory method

Builder

Singleton

Abstract factory

Prototype

Structural patterns:

Adapter

Decorator

Bridge

Composite

Facade

Flyweight

Proxy

Behavioral patterns:

Visitor

Mediator

Observer

Memento

Iterator

Chain Of Responsibility

Strategy

State (this article)

The main essence

Consider a class that represents some logic on the basis of external conditions. The State pattern lets you select appropriate logic that relevant to these external conditions. Some external conditions class (context class) delegates all state-specific logic to the state object. This context class uses its state object instance to perform operations particular to it’s specific internal state.

This pattern also is known as Objects for States.

This pattern includes two main roles:

  • Context — defines the interface of interest to clients and maintains State object that defines the current state.
  • State — defines the interface for behavior associated with a particular state of the Context.

Few words about their collaboration. Context delegates state-specific requests to the current State object.

A context may pass itself as an argument to the State object while handling some specific logic. This lets the State object access the context if necessary.

Clients can configure a Context with State objects (because a Context is the primary interface for clients).

Either Context or State object can decide which state succeeds another and under what circumstances.

Example of use

In this example we will create a CoffeeMachine (Context class) which prepares coffee on the basis of its internal state. After CoffeeMachine instantiation we’re providing it with array of State objects which define the internal state of the Context class and it’s logic to prepare coffee. setState method sets new state of CoffeeMachine class. setStateLibrary fulfills our context class with State objects to work with. process method prepares a coffee cup for us (in case of preparingEspresso or preparingLatteMachiatto state types). In other states just return empty cup.

Also it’s possible to extend the stateLibrary logic to add or remove existing State objects. Another approach is to store State objects in different class and get them from the Context class on demand.

Profit

State pattern localizes state-specific behavior and partition behavior for different states. It puts all behavior associated with a particular state into one object. All State objects are living in one concrete place, so it’s easy to add new object with state specific code.

An alternative to this pattern is to use data values to define internal states and have Context operations check the data explicitly. But in this case we would have like conditional or case statements scattered throughout Context’s implementation. Adding new state could require changing several operations, which complicates maintenance. That’s why a State pattern helps to split concerns across many State Objects and avoid logic overhead in Context class.

Concern separation is actually good if there are many states, which would otherwise necessitate large conditional statements inside the Context class itself. Large conditional statements tend to make the code less explicit, which makes them difficult to modify and extend.

This pattern makes state transitions explicit. There is no internal values that describe something inside Context logic, but all the state specific logic delegated to specific State object itself. Introducing separate objects for different states makes the transitions move explicit. Also, State objects can protect the Context from inconsistent internal states because state transitions are atomic from the Context’s perspective.

State objects can be shared across many Context classes. In such case State objects are actually Flyweights with no intrinsic state, only behavior.

Weak places

This pattern distributes behavior for different stats across several State objects. This increases the number of classes and is less compact than a single Context class.

Conclusion

The State pattern is often similar to the Strategy pattern. But in the second case only functionality can be shared and the strategy should be set explicitly to the Context class and often the Context class instantiated with a desired strategy instance. In case of the State pattern the State object is selected dynamically on the basis of some explicit state change.

State objects are often Singletons without any explicit nesting and mutations. In that case it’s hard to track all the possible State objects in the system. It’s better to keep each such object separate and well documented.

If you found this article helpful, please hit the 👏 button and feel free to comment below!

--

--