State pattern in Java and Python

Behavioral Design Patterns provide solution for the better interaction between objects and how to provide lose coupling and flexibility to extend easily. Following design patterns come under this category.

State pattern lets you change the behavior of a class when the state changes.

State design pattern is used when an Object change its behavior when it’s internal state changes.

The state of an object can be defined as its exact condition at any given point of time, depending on the values of its properties or attributes. The set of methods implemented by a class constitutes the behavior of its instances. Whenever there is a change in the values of its attributes, we say that the state of an object has changed.

When a Context object is first created, it initializes itself with its initial State object. This State object becomes the current State object for the context. By replacing the current State object with a new State object, the context transitions to a new state. When an application object makes a call to a Context method (behavior), it forwards the method call to its current State object.

The benefits of using State pattern to implement polymorphic behavior is clearly visible. The chances of error are less and it’s very easy to add more states for additional behavior. Thus making our code more robust, easily maintainable and flexible. Also State pattern helped in avoiding if-else or switch-case conditional logic in this scenario.

Let's take a look at the diagram definition before we go into more detail.

dp_state.png
  • Context can have a number of internal States, whenever the request() method is called on the Context, the message is delegated to the State to handle.
  • State interface defines a common interface for all concrete states, encapsulating all behaviour associated with a particular state.
  • ConcreteState implements it's own implementation for the request. When a Context changes state, what really happens is that we have a different ConcreteState associated with it.

When to use the State design pattern

  • An object’s behavior depends on its state, and it must change its behavior at run-time depending on that state.
  • Operations have large, multipart conditional statements that depend on the object’s state. This state is usually represented by one or more enumerated constants. Often, several operations will contain this same conditional structure. The State pattern puts each branch of the conditional in a separate class. This lets you treat the object’s state as an object in its own right that can vary independently from other objects.

Java

Let's look at an example of the state pattern. First off, we'll define the EmotionalState interface. It declares two methods, sayHello() and sayGoodbye().

File EmotionalState.java.

// State
public interface EmotionalState {
    public String sayHello();
    public String sayGoodbye();
}

The HappyState class is a Concrete State that implements sayHello() and sayGoodbye() of EmotionalState. These messages are cheerful (representing a happy state).

File HappyState.java.

// Concrete State
public class HappyState implements EmotionalState {
    @Override
    public String sayGoodbye() {
        return "Bye, friend!";
    }

    @Override
    public String sayHello() {
        return "Hello, friend!";
    }
}

The SadState class also implements the EmotionalState interface. The messages are sad (representing a sad state).

File SadState.java.

//Concrete State
public class SadState implements EmotionalState {
    @Override
    public String sayGoodbye() {
        return "Bye. Sniff, sniff.";
    }

    @Override
    public String sayHello() {
        return "Hello. Sniff, sniff.";
    }
}

The Person class is the Context class. It contains an EmotionalState reference to a concrete state. In this example, we have Person implement the EmotionalState reference, and we pass the calls to Person's sayHello() and sayGoodbye() methods on to the corresponding methods on the emotionalState reference. As a result of this, a Person object behaves differently depending on the state of Person (ie, the current EmotionalState reference).

File Person.java.

// Context
public class Person implements EmotionalState {
    EmotionalState emotionalState;

    public Person(EmotionalState emotionalState) {
        this.emotionalState = emotionalState;
    }

    public void setEmotionalState(EmotionalState emotionalState) {
        this.emotionalState = emotionalState;
    }

    @Override
    public String sayGoodbye() {
        return emotionalState.sayGoodbye();
    }

    @Override
    public String sayHello() {
        return emotionalState.sayHello();
    }
}

The Demo class demonstrates the state pattern. First, it creates a Person object with a HappyState object. We display the results of sayHello() and sayGoodbyte() when the person object is in the happy state. Next, we change the person object's state with a SadState object. We display the results of sayHello() and sayGoodbyte(), and we see that in the sad state, the person object's behavior is different.

File Demo.java.

public class Demo {
    public static void main(String[] args) {
        Person person = new Person(new HappyState());
        System.out.println("Hello in happy state: " + person.sayHello());
        System.out.println("Goodbye in happy state: " + person.sayGoodbye());

        person.setEmotionalState(new SadState());
        System.out.println("Hello in sad state: " + person.sayHello());
        System.out.println("Goodbye in sad state: " + person.sayGoodbye());
    }
}

Python 3

import abc

class EmotionalState(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def say_hello(self):
        pass

    @abc.abstractmethod
    def say_goodbye(self):
        pass

class HappyState(EmotionalState):
    def say_goodbye(self):
        return "Bye, friend!"

    def say_hello(self):
        return "Hello, friend!"

class SadState(EmotionalState):
    def say_goodbye(self):
        return "Bye. Sniff, sniff."

    def say_hello(self):
        return "Hello. Sniff, sniff."

class Person(EmotionalState):
    def __init__(self, state):
        self.state = state

    def set_state(self, state):
        self.state = state

    def say_goodbye(self):
        return self.state.say_goodbye()

    def say_hello(self):
        return self.state.say_hello()

if __name__ == '__main__':
    person = Person(HappyState())
    print("Hello in happy state: " + person.say_hello())
    print("Goodbye in happy state: " + person.say_goodbye())

    person.set_state(SadState())
    print("Hello in sad state: " + person.say_hello())
    print("Goodbye in sad state: " + person.say_goodbye())
comments powered by Disqus