State Pattern
State pattern allows an object to alter its behavior when its internal state changes and the object will appear to change its class. For explaining state pattern lets consider the example of a Turnstile at public transportation entrance for subway, where we can use our subway card so that turnstile unlocks provided there are enough funds and we can pass through. Lets model this using state transition diagram.
Turnstile transition diagram
Turnstile basically has two states open and closed. There are many events which happen at these states by which user may end up in the same state or transition to a new state. When the turnstile is closed and there is a payment failed then the turnstile remains in the same state but if we receive a PayOk message then the turnstile opens and transitions to open state. Also, if a person tries to enter the turnstile in closed state then the turnstile should remain closed.
Once the turnstile is opened it allows one person to pass through it. Once the person leaves the turnstile it goes back to the closed state. But if we receive a PayOk message on open state when no person has passed through it the turnstile should remain open and no funds must be deducted. Even if we receive the PayFail message in open state the turnstile should remain in open state.
Actions/States | Enter | PayOk | PayFailed |
Closed | Closed | Open | Closed |
Open | Closed | Open | Open |
Above is a state diagram for the states we discussed along with the action and states table showing different possibilities. Designing above without the use of state pattern will lead to having lot more conditionals in the code and eventually duplicating it in multiple places for checking the state. Also, this would be become more difficult as the number of states increases as there would be a greater number of combinations to validate against. State Pattern allows us to solve this problem by narrowing down to each state without allowing duplication.
Generalized UML Diagram
Let’s look at the UML diagram in context of our example we discussed. There is a context which can have states, in our case it was turnstile. We have an interface State which has multiple concrete states and the context has these states like the turnstile can have states and these states can be different like Open, closed etc, and all these states are treated uniformly. The State class has a method Handle () which are implemented by the concrete classes and they have their own implementation. To emphasize the interface of Context class to be different to State class we call the method in Context as request () and the method in State as Handle ().
Let’s modify the above generalized UML diagram to our example and it would look like above. We are calling turnstile as Gate for our convenience. Gate can receive the following messages in our example which are Enter, Pay Ok and PayFailed which replaces our request in our Context. When a enter method is called on Gate instance that method is delegated to enter method in GateState method. GateState has a reference back to Gate which will allow us to changeState.
Code
Below is the code implementation for our turnstile example. We have Gate class which has the methods for the messages it receives. The implementation of these methods will be to delegate to methods of Concrete GateState classes. There is a ChangeState method on the Gate class which enables to set the changed state. All the concrete implementations have the necessary logic for each of the methods and they call ChangeState method if they must change state.
GitHub: StatePattern
#17DayOf100DaysOfCode
Comments