State
Lets an object alter its behavior when its internal state changes. It appears as if the object changed its class.
classDiagram
class Context {
-State state
+changeState(State)
+request()
}
class State {
<<interface>>
+handle(Context)
}
class ConcreteStateA {
+handle(Context)
}
class ConcreteStateB {
+handle(Context)
}
Context o-- State
State <|.. ConcreteStateA
State <|.. ConcreteStateB
ConcreteStateA --> Context
ConcreteStateB --> Context
When to use
Use the State pattern when you have an object that behaves differently depending on its current state, the number of states is enormous, and the state-specific code changes frequently. Use when you have a massive conditional (switch/if) that selects the proper behavior depending on the values of the object’s fields.
Explanation
Problem
Imagine that you’re creating a Document class. A document can be in one of three states: Draft, Moderation, and Published. The publish method of the document works a little bit differently in each state:
- In
Draft, it moves the document to moderation. - In
Moderation, it makes the document public, but only if the current user is an administrator. - In
Published, it doesn’t do anything at all.
You could create a massive state machine with conditionals. But as you add more states and transitions, the code becomes unmaintainable.
Solution
The State pattern suggests that you create new classes for all possible states of an object and extract all state-specific behaviors into these methods. Instead of implementing all behaviors on its own, the original object, called context, stores a reference to one of the state objects that represents its current state, and delegates all the state-related work to that object.
Real world problem
- Media Player: Play Button works differently if playing, paused, or locked.
- Order Status: Pending -> Paid -> Shipped. ‘Cancel’ works differently in each.
- TCP Connection: Established, Listening, Closed.
Pros and Cons
| Pros | Cons |
|---|---|
| - Single Responsibility Principle: Organize the code related to particular states into separate classes. - Open/Closed Principle: Introduce new states without changing existing state classes or the context. - Simplification: Simplify the code of the context by eliminating bulky state machine conditionals. |
- Overkill: Applying the pattern can be overkill if a state machine has only a few states or rarely changes. |
Comparison
- Bridge, State, Strategy: All based on composition. State manages state transitions. Strategy manages algorithm switching. Bridge manages abstraction/implementation separation.
- Singleton: State objects are often Singletons.
Code example
Typescript
Php