×

State Design Pattern With Python

State Design Patterns Python Feature

State Design Pattern is prefered to use in the situation where the functionality of an interface depends upon its current state.

Consider a mobile phone if a mobile phone is connected to the internet it can be used to make video calls, stream online videos or do online web surfing etc., but if a phone is disconnected from the internet it will not be able to do any online stuff.

That is, functionalities offered by the mobile phone depends upon its state — Connected to the internet or not.

State pattern offers to change the behaviour of a class when a class changes its state. State Pattern enables the implementation of somewhat Polymorphic behaviour.

State Pattern helps to avoid the usage of if-else and switch-case conditional logic making our code more robust, loosely coupled and increasing the maintainability.

Components of State Pattern

  • Context: Context interface represents entity(i.e class) that can have various states. Context interface have reference to all states possible. And, have a method request() to switch between various states possible.
class Context:
    _state = None
    """ A reference to the current state."""

    def __init__(self, state):
        self.transition_to(state)

    def transition_to(self, state):
        """Method to make transition"""

        print(f"Context: Transition to {type(state).__name__}")
        self._state = state
        self._state.context = self
    
    def request1(self):
        self._state.handle1()

    def request2(self):
        self._state.handle2()
  • State interface: An abstract interface which defines a common interface for all concrete states, encapsulating all behaviour associated with a particular state.
class State:
    """An abstract class for Concrete sub-classes"""

    @property
    def context(self):
        return self._context

    @context.setter
    def context(self, context: Context):
        self._context = context

    @abstractmethod
    def handle1(self):
        pass

    @abstractmethod
    def handle2(self):
        pass
  • ConcreteState classes: These classes implements their own implementation for the request. When a Context changes state, what really happens is that we have a different ConcreteState associated with it.
class State_A(State):
    def handle1(self):
        print("State_A handles request1.")
        print("State_A wants to change the state of the context.")
        self.context.transition_to(State_B())

    def handle2(self):
        print("State_A handles request2.")
class State_B(State):
    def handle1(self):
        print("State_B handles request1.")

    def handle2(self):
        print("State_B handles request2.")
        print("State_B wants to change the state of the context.")
        self.context.transition_to(State_A())
  • The driver code will be like:
if __name__ == "__main__":
    # The Driver code.

    context = Context(State_A())
    context.request1()
    context.request2()

Example

Consider a Person. Let a person can have two states Happy and Sad.

  • Abstract base class for various states.
class EmotionalState():
    @abstractmethod
    def say_hello(self):
        pass

    @abstractmethod
    def say_goodbye(self):
        pass
  • Actual state class
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."
  • Now the Context class Person
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())
Output
Hello in happy state: Hello, friend!
Goodbye in happy state: Bye, friend!
Hello in sad state: Hello. Sniff, sniff.
Goodbye in sad state: Bye. Sniff, sniff.

An object’s behaviour depends on its state, and it must change its behaviour at run-time depending on that state. In such scenarios, the State Design Pattern can be used.