Â
In this post, I will use the State Pattern to alter the behaviors of a hero when it is under attack or being silenced. State pattern allow us to encapsulate states into separate classes and delegate behaviors to the object representing the current state.
See code at Github repo (https://github.com/yangju2011/design-patterns). Code is adapted from Head First Design Patterns: A Brain-Friendly Guide 1st Edition by Eric Freeman and Elisabeth Robson.
Table of Contents
State Pattern
Definition
Allows an object to alter its behaviors when its internal state changes. The object will appear to change in class. It encapsulates states into separate classes and delegates behaviors to the object representing the current state.
State pattern and state machine
These two concepts are closely related and sometimes used interchangeably. However, they are different in their focus.
- State pattern abstracts the states and focuses on varying behaviors of a state. It doesn’t mean to address the transition.
- State machine abstracts the state diagram and focuses on the transition between states. It doesn’t mean to address the behaviors in each state.
See Wikipedia and StackOverflow.
HeroStateV1
In this example, a Hero
 can be in one of the 3 states: ALIVE, DEAD, SILENCED
 defined in HeroContext
When a Hero is ALIVE
 with hp>0
, it can attack
, and can be attacked
 and silenced
.
- WhenÂ
attacked
, itsÂhp
 is decreased byÂdamage
. IfÂhp<=0
, a Hero enters the state ofÂDEAD
 withÂhp=0
. - WhenÂ
silenced
, a Hero enters the state ofÂSILENCED
.
When a Hero is DEAD
 with hp=0
, it can be revived
.
- WhenÂ
revived
, a Hero enters the state ofÂALIVE
 with full hp.
When a Hero is SILENCED
, it can be attacked
 and recovered
.
- WhenÂ
attacked
, itsÂhp
 is decreased byÂdamage
. IfÂhp<=0
, a Hero enters the state ofÂDEAD
 withÂhp=0
. - WhenÂ
recovered
, a Hero enters the state ofÂALIVE
.
If we have a new State
 or a new behavior
, we will have to modify code in HeroContext
.
Example output:
Hero Context
================
Hero HP 10
Hero is alive
...Hero is being attacked with damage: 5
Hero Context
================
Hero HP 5
Hero is alive
...Hero is attacking others.
Hero Context
================
Hero HP 5
Hero is alive
...Hero is already recovered, and cannot be recovered.
...Hero is already alive, and cannot be revived.
...Hero is being attacked with damage: 10
Hero Context
================
Hero HP 0
Hero is dead
...Hero is dead, and cannot attack others.
...Hero is dead, and cannot be attacked.
...Hero is dead, and cannot be recovered.
...Hero is dead, and cannot be silenced.
...Hero is being revived.
Hero Context
================
Hero HP 10
Hero is alive
...Hero is being silenced.
Hero Context
================
Hero HP 10
Hero is silenced
...Hero is silenced and cannot attack others.
...Hero is already silenced and cannot be silenced again.
...Hero is already alive but silenced, and cannot be revived.
...Hero is being attacked with damage: 5
Hero Context
================
Hero HP 5
Hero is silenced
...Hero is being recovered.
Hero Context
================
Hero HP 5
Hero is alive
...Hero is being silenced.
...Hero is being attacked with damage: 10
Hero Context
================
Hero HP 0
Hero is dead
...Hero is being revived.
Hero Context
================
Hero HP 10
Hero is alive
HeroStateV2
Here we are applying the State Pattern with HeroContext
 and State
- create aÂ
State
 interface with all possible behaviors (actions). - implement theÂ
State
 interface in concrete classes, each class has its own implementation for all bebaviors. HeroContext
 includes all differentÂState
 and callsÂstate.handle()
. All behaviorsÂhandle()
 are delegated toÂState
.
We can think of the State Pattern as an alternative to having a lot of if-else
 conditions in the context.
Using the State Pattern, we encapsulate what varies (behaviors) in State
 classes and usually have to create more classes in the design. The client HeroContext
 knows very little about the state object. The current state of the context changes across a set of state objects to reflect its internal state, and the behaviors of the context changes as well. When we call context.handle()
, depending on the current state of the context, the behaviors vary.
Example output is the same as before.
HeroStateV3
Here we have two new behaviors disappear, appear
 (in red) associated with a new state InvisibleState
.
Using State Pattern, we can keep HeroContext
 mostly unchanged.
- When we have a newÂ
behavior
, we can modify theÂState
 interface and implement this behavior inÂState
 classes. - When we have a newÂ
State
, we can easily create a new class implementingÂState
 interface and define its behaviors.
The state transition graph is shown below:
Example output:
Hero Context
================
Hero HP 10
Hero is alive
...Hero is being attacked with damage: 5
Hero Context
================
Hero HP 5
Hero is alive
...Hero is attacking others.
Hero Context
================
Hero HP 5
Hero is alive
...Hero is already visible, and cannot appear.
...Hero is already recovered, and cannot be recovered.
...Hero is already alive, and cannot be revived.
...Hero disappears.
Hero Context
================
Hero HP 5
Hero is invisible
...Hero is already invisible, and cannot disappear.
...Hero is invisible and cannot be attacked.
...Hero is invisible and cannot be silenced.
...Hero is already recovered, and cannot be recovered.
...Hero is already alive, and cannot be revived.
...Hero is attacking others.
Hero Context
================
Hero HP 5
Hero is invisible
...Hero appears.
Hero Context
================
Hero HP 5
Hero is alive
...Hero is being attacked with damage: 10
Hero Context
================
Hero HP 0
Hero is dead
...Hero is dead, and cannot appear.
...Hero is dead, and cannot disappear.
...Hero is dead, and cannot attack others.
...Hero is dead, and cannot be attacked.
...Hero is dead, and cannot be recovered.
...Hero is dead, and cannot be silenced.
...Hero is being revived.
Hero Context
================
Hero HP 10
Hero is alive
...Hero is being silenced.
Hero Context
================
Hero HP 10
Hero is silenced
...Hero is already visible, and cannot appear.
...Hero is silenced and cannot attack others.
...Hero is already silenced and cannot be silenced again.
...Hero is already alive but silenced, and cannot be revived.
...Hero disappears.
Hero Context
================
Hero HP 10
Hero is invisible
...Hero appears.
...Hero is being attacked with damage: 5
Hero Context
================
Hero HP 5
Hero is alive
...Hero is already recovered, and cannot be recovered.
Hero Context
================
Hero HP 5
Hero is alive
...Hero is being silenced.
...Hero is being attacked with damage: 10
Hero Context
================
Hero HP 0
Hero is dead
...Hero is being revived.
Hero Context
================
Hero HP 10
Hero is alive
Real life use cases
- Uber trip booking:Â State Machine Design pattern â Part 2: State Pattern vs. State Machine
- E-commerce ordering, food ordering, and trip booking:Â Finite State Machines + Android + Kotlin = Good Times
- Mobile app UI activity flow:Â State Machine Design pattern âPart 1: When, Why & How
- Workflow engine (sequential pattern):Â WORKFLOW ENGINE VS. STATE MACHINE