Listen to this post
The system needs to behave differently when it is in various conditions (states). This often occurs when a program is modal.
The software in an ATM is operated by the end user through an interface that has various buttons and a keypad for entering numbers. The buttons will do various things depending on where the user is in the workflow. A given button might be used to select “withdraw funds” and then that same button might be subsequently used to select which account to withdraw from. Similarly, the keypad that allowed the user to enter their pin could be used later to enter the amount of a deposit.
The State Pattern could be used to manage these changes. As the system is used, the state object is changed and thus all behaviors transition to the appropriate versions.
Qualities and principles
Each state object encapsulates the behaviors that are appropriate for one mode of the system, making them easier to understand and change. The clients couple only to the state interface. All state objects are interchangeable with one another. If there are common behaviors (exitKeyPressed() in the example) these can be implemented in the base class, avoiding redundancies. New states can be added and/or existing states removed with little or no impact on clients.
Each state object can be tested on its own, through its public interface. The client can be tested using a Mock of the state interface.
Questions and concerns
One issue that must always be resolved is how the transitions from state to state are to be handled. This will change based on the context of the domain, but some possibilities include:
- Each state object, when used, can return the appropriate next state object. This does tend to couple them to each other, however.
- A routine in the client can contain the logic to change states. Care is needed to prevent redundancies if multiple clients require the same or similar logic.
- A state manager class can be used by all clients to manage these transitions.