Design Pattern: State Pattern

Summary

Pattern NameState Pattern
Pattern TypeBehavioral Pattern
ScopeObject
Other NamesObjects for States
TaglineChange object behavior on state change
Use casesWhen we want to use a different behavior
of an object depending on a certain state
Related PatternsFlyweight
Strategy
Difficulty LevelMedium
Implementations

Definition

In State pattern, an object (context object) changes its behavior when a certain internal state is changed. On state change the context object will change its behavior completely- like for performing a certain operation the context object will use different objects when the state is changed.

This state change is managed internally so the client which is using the context object might not be aware of the state change.

State pattern can increase the number of classes used for the implementation. That would make the maintenance of the system difficult.

Use Cases

Here are the cases when we need to use the State pattern-

  • When the behavior of an object depends on a certain state, and on that state changes the behavior needs to be changed at run-time.
  • When there are heavy multipart operations that depend on the object state.

Implementation

State pattern has 3 elements.

  1. Context: class which holds the state. The behavior of this object is changed on state change.
  2. State Interface: interface (or abstract class) for the state classes.
  3. Concrete State Class: classes that are used by the context class. The context object will use any of these class object based on the state.

Follow the steps below to implement State pattern:

  1. Create an interface or abstract class for the state. Declare a few functions.
  2. Create multiple classes and implement the state interface. Implement the functions. Maintain a variable to hold the context so that can be used from the state when required.
  3. Create context class. Implement methods to set and get the state. Add methods to perform actions, in the function call functions from the state.

Examples

Example #1: Simplest State Pattern

Here is the simplest example of State pattern

States

The state is an abstract class, which accepts a context object in constructor. When set, it will store a reference to the context and also will call the setState method of the context and pass current object.

Concrete state classes extends the state class, and implements the operation methods.

// State Interface (Abstract class in this case)

abstract class State
    
    var context: Context 

    constructor(contextParam: Context)
        context = contextParam

        context.setState(this)
    end constructor

    abstract void actionOne()

    abstract void actionTwo()

end class


// Concrete State One

class ConcreteStateOne extends State

    constructor(contextParam: Context)
        super(context)
    end constructor

    method actionOne()
        output "Calling 'actionOne' of - 'ConcreteStateOne'"
    end method

    method actionTwo()
        output "Calling 'actionTwo' of - 'ConcreteStateOne'"
    end method

end class


// Concrete State Two

class ConcreteStateTwo extends State

    constructor(contextParam: Context)
        super(context)
    end constructor

    method actionOne()
        output "Calling 'actionOne' of - 'ConcreteStateTwo'"
    end method

    method actionTwo()
        output "Calling 'actionTwo' of - 'ConcreteStateTwo'"
    end method

end class

Context

Context has a setState method that stores the passed state object. Also, implement methods relevant to the state, these methods will call the methods from the state for performing operations.

// Context

class Context

    var state: State

    method setState(stateParam: State)
        state = stateParam
    end method

    method getState(): State
        return state
    end method

    method performActionOne()
        state.actionOne()
    end method

    method performActionTwo()
        state.actionTwo()
    end method

end class

Demo

Create a context object. Then pass that to state objects. That will call the setState from the context and state is set as that state object.

We can call methods from context object, which in turn will call the methods from the state object.

var context: Context = new Context()
new ConcreteStateOne(context)

context.performActionOne()

new ConcreteStateTwo(context)

context.performActionOne()
context.performActionTwo()

Output

Calling 'actionOne' of - 'ConcreteStateOne'

Calling 'actionOne' of - 'ConcreteStateTwo'
Calling 'actionTwo' of - 'ConcreteStateTwo'

Example #2: Order State

Let’s consider another example that is a little complex. We are demonstrating an order processing state.

Order State

// Order State

abstract class OrderState

    var context: OrderContext

    constructor(contextParam: OrderContext)
        context = contextParam

        context.setState(this)
    end constructor

    abstract void process()

end class


// Order Check State

class OrderCheckState extends OrderState

    constructor(contextParam: OrderContext)
        super(contextParam)
    end constructor

    method process()
        // Write code to process the order
        output "Checking the order validity and other information")

        context.setState(context.getOrderInProgressState())
    end method

end class


// OrderInProgressState.java

class OrderInProgressState extends OrderState

    constructor(contextParam: OrderContext)
        super(contextParam)
    end constructor

    method process()
        // Write code to process the order
        output "Processing the order")

        context.setState(context.getOrderDeliverState())
    end method

end class


// Order Deliver State

class OrderDeliverState extends OrderState

    constructor(contextParam: OrderContext)
        super(contextParam)
    end constructor

    method process() {
        // Write code to process the order
        output "Delivering the order"

        context.setState(context.getOrderReceiveState())
    end method

end class


// Order Receive State

class OrderReceiveState extends OrderState

    constructor(contextParam: OrderContext)
        super(contextParam)
    enc constructor

    method process() {
        // Write code to process the order
        output "Order received"

        context.setState(null)
    end method

end class

Order Context Class

// Order Context

class OrderContext
    var state: OrderState
    var orderCheckState: OrderState
    var orderInProgressState: OrderState
    var orderDeliverState: OrderState
    var orderReceiveState: OrderState

    consctructor()
        orderCheckState = new OrderCheckState(this)
        orderInProgressState = new OrderInProgressState(this)
        orderDeliverState = new OrderDeliverState(this)
        orderReceiveState = new OrderReceiveState(this)

        // Set the placed state as default
        state = orderCheckState
    end constructor

    method setState(stateParam: OrderState)
        state = stateParam
    end method

    method getState(): OrderState
        return state
    end method

    method getOrderCheckState(): OrderState
        return orderCheckState;
    end method

    method getOrderInProgressState(): OrderState
        return orderInProgressState;
    end method

    method getOrderDeliverState(): OrderState
        return orderDeliverState;
    end method

    method getOrderReceiveState(): OrderState
        return orderReceiveState;
    end method

    method runNextProcess() {
        if (state != null)
            state.process()
        else
            output "Order processing complete"
        end if
    end method

end class

Demo

// Demo

var order: OrderContext = new OrderContext()

order.runNextProcess()
order.runNextProcess()
order.runNextProcess()
order.runNextProcess()

// Trying to process after all steps are complete
order.runNextProcess()

Output

Checking the order validity and other information
Processing the order
Delivering the order
Order received


Order processing complete

Code Implementations

Use the following links to check State pattern implementation in specific programming languages.

Leave a Comment


The reCAPTCHA verification period has expired. Please reload the page.