Skip to content

State Design Pattern

Video Lecture

Section Video Links
State Overview State Overview State Overview State Overview 
State Use Case State Use Case State Use Case State Use Case 
__call__ Attribute Dunder __call__ Attribute Dunder __call__ Attribute Dunder __call__ Attribute 

Overview

...Refer to Book, Videos or Medium Membership for extra content.

Terminology

...Refer to Book, Videos or Medium Membership for extra content.

State UML Diagram

State UML Diagram

Source Code

...Refer to Book, Videos or Medium Membership for extra content.

./state/state_concept.py

 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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# pylint: disable=too-few-public-methods
"The State Pattern Concept"
from abc import ABCMeta, abstractmethod
import random

class Context():
    "This is the object whose behavior will change"

    def __init__(self):
        self.state_handles = [ConcreteStateA(),
                              ConcreteStateB(),
                              ConcreteStateC()]
        self.handle = None

    def request(self):
        """A method of the state that dynamically changes which
        class it uses depending on the value of self.handle"""
        self.handle = self.state_handles[random.randint(0, 2)]
        return self.handle

class IState(metaclass=ABCMeta):
    "A State Interface"

    @staticmethod
    @abstractmethod
    def __str__():
        "Set the default method"

class ConcreteStateA(IState):
    "A ConcreteState Subclass"

    def __str__(self):
        return "I am ConcreteStateA"

class ConcreteStateB(IState):
    "A ConcreteState Subclass"

    def __str__(self):
        return "I am ConcreteStateB"

class ConcreteStateC(IState):
    "A ConcreteState Subclass"

    def __str__(self):
        return "I am ConcreteStateC"

# The Client
CONTEXT = Context()
print(CONTEXT.request())
print(CONTEXT.request())
print(CONTEXT.request())
print(CONTEXT.request())
print(CONTEXT.request())

Output

1
2
3
4
5
6
python.exe ./state/state_concept.py
I am ConcreteStateB
I am ConcreteStateA
I am ConcreteStateB
I am ConcreteStateA
I am ConcreteStateC

State Example Use Case

...Refer to Book, Videos or Medium Membership for extra content.

State Example Use Case UML Diagram

State Example Use Case UML Diagram

Source Code

./state/client.py

 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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# pylint: disable=too-few-public-methods
"The State Use Case Example"
from abc import ABCMeta, abstractmethod


class Context():
    "This is the object whose behavior will change"

    def __init__(self):

        self.state_handles = [
            Started(),
            Running(),
            Finished()
        ]
        self._handle = iter(self.state_handles)

    def request(self):
        "Each time the request is called, a new class will handle it"
        try:
            self._handle.__next__()()
        except StopIteration:
            # resetting so it loops
            self._handle = iter(self.state_handles)


class IState(metaclass=ABCMeta):
    "A State Interface"

    @staticmethod
    @abstractmethod
    def __call__():
        "Set the default method"


class Started(IState):
    "A ConcreteState Subclass"

    @staticmethod
    def method():
        "A task of this class"
        print("Task Started")

    __call__ = method


class Running(IState):
    "A ConcreteState Subclass"

    @staticmethod
    def method():
        "A task of this class"
        print("Task Running")

    __call__ = method


class Finished(IState):
    "A ConcreteState Subclass"

    @staticmethod
    def method():
        "A task of this class"
        print("Task Finished")

    __call__ = method


# The Client
CONTEXT = Context()
CONTEXT.request()
CONTEXT.request()
CONTEXT.request()
CONTEXT.request()
CONTEXT.request()
CONTEXT.request()

Output

1
2
3
4
5
6
python.exe ./state/client.py
Task Started
Task Running
Task Finished
Task Started
Task Running

New Coding Concepts

__call__ Dunder Attribute

Overloading the __call__ attribute makes an instance of a class callable like a function when by default it isn't. You need to call a method within the class directly.

1
2
3
4
5
6
7
class ExampleClass:
    @staticmethod
    def do_this_by_default():
        print("doing this")

EXAMPLE = ExampleClass()
EXAMPLE.do_this_by_default() # needs to be explicitly called to execute

If you want a default method in your class, you can point to it by using by the __call__ attribute.

1
2
3
4
5
6
7
8
9
class ExampleClass:
    @staticmethod
    def do_this_by_default():
        print("doing this")

    __call__ = do_this_by_default

EXAMPLE = ExampleClass()
EXAMPLE() # function now gets called by default

Summary

...Refer to Book, Videos or Medium Membership for extra content.