Let's understand all three participants with a simple example. Say, we want to implement a TV remote with a simple button to perform on/off actions. If the TV is on, the remote button will switch off the TV and vice versa. In this case, the State
interface will define the method (say, doThis()
) to perform actions such as switching on/off the TV. We also need to define ConcreteClass
for different states. In this example, we have two major states, StartState
and StopState
, which indicate when the TV is switched on and the state in which the TV is switched off, respectively.
For this scenario, the TVContext
class will implement the State
interface and keep a reference to the current state. Based on the request, TVContext
forwards the request to ConcreteState
, which implements the actual behavior (for a given state) and performs the necessary action. So, in this case, the base state is StartState
(as defined earlier) and the request received by the TVContext
class is to switch Off the TV. TVContext
class understands the need and accordingly forwards the request to StopState
concrete class which inturn calls the doThis()
method to actually switch off the TV:
from abc import abstractmethod, ABCMeta class State(metaclass=ABCMeta): @abstractmethod def doThis(self): pass class StartState (State): def doThis(self): print("TV Switching ON..") class StopState (State): def doThis(self): print("TV Switching OFF..") class TVContext(State): def __init__(self): self.state = None def getState(self): return self.state def setState(self, state): self.state = state def doThis(self): self.state.doThis() context = TVContext() context.getState() start = StartState() stop = StopState() context.setState(stop) context.doThis()
Here is the output for the preceding code:
Let's now take a look at a real-world use case for the State design pattern. Think of a computer system (desktop/laptop). It can have multiple states such as On
, Off
, Suspend
, or Hibernate
. Now, if we want to manifest these states with the help of State design pattern, how will we do it?
Say, we start with the ComputerState
interface:
name
and allowed
. The name
attribute represents the state of the object, and allowed
is a list that defines the state's object, which it is allowed to get into.switch()
method, which will actually change the state of the object (in this case, the computer).Let's take a look at the code implementation of the ComputerState
interface:
class ComputerState(object): name = "state" allowed = [] def switch(self, state): if state.name in self.allowed: print('Current:',self,' => switched to new state',state.name) self.__class__ = state else: print('Current:',self,' => switching to',state.name,'not possible.') def __str__(self): return self.name
Let's now take a look at ConcreteState
, which implements the State
interface. We will define four states:
On
: This switches on the computer. The allowed states here are Off
, Suspend
, and Hibernate
.Off
: This switches off the computer. The allowed state here is just On
.Hibernate
: This state puts the computer in the hibernate mode. The computer can only get switched on when it's in this state.Suspend
: This state suspends the computer, and once the computer is suspended, it can be switched on.Let's now take a look at the code:
class Off(ComputerState): name = "off" allowed = ['on'] class On(ComputerState): name = "on" allowed = ['off','suspend','hibernate'] class Suspend(ComputerState): name = "suspend" allowed = ['on'] class Hibernate(ComputerState): name = "hibernate" allowed = ['on']
Now, we explore the context class (Computer
). The context does two main things:
__init__()
: This method defines the base state of the computerchange()
: This method will change the state of the object, and the actual change in behavior is implemented by the ConcreteState
classes (on
, off
, suspend
, and hibernate
)Here is the implementation of the preceding methods:
class Computer(object): def __init__(self, model='HP'): self.model = model self.state = Off() def change(self, state): self.state.switch(state)
The following is the code for the client. We create the object of the Computer
class (Context
) and pass a state to it. The state can be either of these: On
, Off
, Suspend
, and Hibernate
. Based on the new state, the context calls its change(state)
method, which eventually switches the actual state of the computer:
if __name__ == "__main__": comp = Computer() # Switch on comp.change(On) # Switch off comp.change(Off) # Switch on again comp.change(On) # Suspend comp.change(Suspend) # Try to hibernate - cannot! comp.change(Hibernate) # switch on back comp.change(On) # Finally off comp.change(Off)
Now, we can observe the following output:
__class__
is a built-in attribute of every class. It is a reference to the class. For instance, self.__class__.__name__
represents the name of the class. In this example, we use __class__ attribute
of Python to change the State
. So, when we pass the state to the change()
method, the class of the objects gets dynamically changed at runtime. The comp.change(On)
code, changes the object state to On
and subsequently to different states like Suspend
, Hibernate
, and Off
.
18.222.3.255