Skip to content

State pattern

Quality Score

Overall Score: 9.3/10 ⭐ Excellent

  • Technical Accuracy: 33/35
  • Code Quality: 25/25
  • Educational Value: 21/25
  • Documentation: 14/15

Last reviewed: July 2, 2026

State pattern is a behavioral design pattern that allows an object to change its default behavior when its internal state changes. Once the state of an object is updated, the object will react differently to the same input. It is very useful when you expect an object to react differently based on its state.

The simple use case of state pattern is a state machine. A state machine is an algorithm that represents a set of states and the transitions between those states. Then, state machine can be used to model the behavior of an object that can be in different states. For example, a user can be in different states like pending, active, inactive and closed. Once the user changes from one state to another, the user can or cannot perform different actions (like login is only allowed when the user is active).

The main benefits of this pattern are:

  • Encapsulation: Each state is self-contained and easier to test
  • Flexibility: Add new states without modifying existing code
  • Maintainability: Each state is a separate class, making it easier to maintain and understand the code

The state pattern is composed of three main components:

State Interface

Defines the common interface for all supported states. The responsibility is to know how to execute, not when to use the state. The interface is usually implemented as an abstract class.

src.advanced.state_pattern.UserState

Bases: ABC

Abstract base class for a user state.

Source code in src/advanced/state_pattern/state.py
 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
class UserState(ABC):
    """Abstract base class for a user state."""

    @abstractmethod
    def enable(self, user: "User") -> None:
        """Handle the enable action for the user state.

        Args:
            user (User): The user instance.
        """
        pass

    @abstractmethod
    def disabled(self, user: "User") -> None:
        """Handle the disabled action for the user state.

        Args:
            user (User): The user instance.
        """
        pass

    @abstractmethod
    def close(self, user: "User") -> None:
        """Handle the close action for the user state.

        Args:
            user (User): The user instance.
        """
        pass

    @abstractmethod
    def login(self, user: "User") -> bool:
        """Handle the login action for the user state.

        Args:
            user (User): The user instance.

        Returns:
            bool: True if login is successful, False otherwise.
        """
        pass

Concrete State

Implements the behavior associated with a state of the context. The responsibility is to know how to execute and where state to transition, not when to use the state.

src.advanced.state_pattern.Pending

Bases: UserState

Concrete state representing a pending user.

A pending user is not yet been activated or disabled. It can transition to active or closed states.

Source code in src/advanced/state_pattern/state.py
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
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
class Pending(UserState):
    """Concrete state representing a pending user.

    A pending user is not yet been activated or disabled.
    It can transition to active or closed states.
    """

    def enable(self, user: "User") -> None:
        """Transition from pending to active state.

        Args:
            user (User): The user instance.
        """
        logging.info("Transitioning from pending to active state.")
        user.set_state(Active())

    def disabled(self, user: "User") -> None:  # noqa: ARG002
        """Pending state cannot be disabled.

        Args:
            user (User): The user instance.
        """
        logging.info("Pending state cannot be disabled.")

    def close(self, user: "User") -> None:
        """Transition from pending to closed state.

        Args:
            user (User): The user instance.
        """
        logging.info("Transitioning from pending to closed state.")
        user.set_state(Closed())

    def login(self, user: "User") -> bool:  # noqa: ARG002
        """Pending users cannot login.

        Args:
            user (User): The user instance.

        Returns:
            bool: False, pending users must be activated first.
        """
        logging.info("Pending users cannot login. Activate first.")
        return False

src.advanced.state_pattern.Active

Bases: UserState

Concrete state representing an active user.

An active user can transition to inactive or closed states.

Source code in src/advanced/state_pattern/state.py
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
class Active(UserState):
    """Concrete state representing an active user.

    An active user can transition to inactive or closed states.
    """

    def enable(self, user: "User") -> None:  # noqa: ARG002
        """Active state cannot be enabled again.

        Args:
            user (User): The user instance.
        """
        logging.info("Active state cannot be enabled again.")

    def disabled(self, user: "User") -> None:
        """Transition from active to inactive state.

        Args:
            user (User): The user instance.
        """
        logging.info("Transitioning from active to inactive state.")
        user.set_state(Inactive())

    def close(self, user: "User") -> None:
        """Transition from active to closed state.

        Args:
            user (User): The user instance.
        """
        logging.info("Transitioning from active to closed state.")
        user.set_state(Closed())

    def login(self, user: "User") -> bool:
        """Active users can login successfully.

        Args:
            user (User): The user instance.

        Returns:
            bool: True, login successful.
        """
        logging.info("User %s logged in successfully.", user.username)
        return True

src.advanced.state_pattern.Inactive

Bases: UserState

Concrete state representing an inactive user.

An inactive user can transition to active or closed states.

Source code in src/advanced/state_pattern/state.py
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
class Inactive(UserState):
    """Concrete state representing an inactive user.

    An inactive user can transition to active or closed states.
    """

    def enable(self, user: "User") -> None:
        """Transition from inactive to active state.

        Args:
            user (User): The user instance.
        """
        logging.info("Transitioning from inactive to active state.")
        user.set_state(Active())

    def disabled(self, user: "User") -> None:  # noqa: ARG002
        """Inactive state cannot be disabled again.

        Args:
            user (User): The user instance.
        """
        logging.info("Inactive state cannot be disabled again.")

    def close(self, user: "User") -> None:
        """Transition from inactive to closed state.

        Args:
            user (User): The user instance.
        """
        logging.info("Transitioning from inactive to closed state.")
        user.set_state(Closed())

    def login(self, user: "User") -> bool:  # noqa: ARG002
        """Inactive users cannot login.

        Args:
            user (User): The user instance.

        Returns:
            bool: False, inactive users must be reactivated first.
        """
        logging.info("Inactive users cannot login. Reactivate first.")
        return False

src.advanced.state_pattern.Closed

Bases: UserState

Concrete state representing a closed user.

A closed user cannot transition to any other state.

Source code in src/advanced/state_pattern/state.py
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
class Closed(UserState):
    """Concrete state representing a closed user.

    A closed user cannot transition to any other state.
    """

    def enable(self, user: "User") -> None:  # noqa: ARG002
        """Closed state cannot be enabled.

        Args:
            user (User): The user instance.
        """
        logging.info("Closed state cannot be enabled.")

    def disabled(self, user: "User") -> bool:  # noqa: ARG002
        """Closed state cannot be disabled.

        Args:
            user (User): The user instance.
        """
        logging.info("Closed state cannot be disabled.")

    def close(self, user: "User") -> bool:  # noqa: ARG002
        """Closed state is already closed.

        Args:
            user (User): The user instance.
        """
        logging.info("Closed state is already closed.")

    def login(self, user: "User") -> bool:  # noqa: ARG002
        """Closed users cannot login.

        Args:
            user (User): The user instance.

        Returns:
            bool: False, closed accounts cannot login.
        """
        logging.info("Closed accounts cannot login.")
        return False

Context

Maintains an instance of the class that defines the current state. The responsibility is to know when to use the state, not how to execute it.

src.advanced.state_pattern.User

Context class that maintains a reference to a UserState instance.

Source code in src/advanced/state_pattern/state.py
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
class User:
    """Context class that maintains a reference to a UserState instance."""

    def __init__(self, username: str) -> None:
        """Initialize the User with pending state."""
        self._state: UserState = Pending()
        self.username = username

    def set_state(self, state: UserState) -> None:
        """Set a new state for the user.

        Args:
            state (UserState): The new state to set.
        """
        self._state = state

    def enable(self) -> None:
        """Delegate the enable action to the current state."""
        self._state.enable(self)

    def disabled(self) -> None:
        """Delegate the disabled action to the current state."""
        self._state.disabled(self)

    def close(self) -> None:
        """Delegate the close action to the current state."""
        self._state.close(self)

    def login(self) -> bool:
        """Delegate the login action to the current state.

        Returns:
            bool: True if login successful, False otherwise.
        """
        return self._state.login(self)

Some real world applications of the state pattern are: orders and e-shopping carts, workflows, finance, media streaming... everything that can be modeled as a state machine can benefit from the state pattern.