JS: Polymorphism
Theory: Pattern State
The State pattern is a prime example of replacing conditional constructs with subtype polymorphism. It's widely used and can reduce the complexity of your code. Let's look at the behavior of phone screens as an example.
Not all phones behave the same way, but we needed to choose a specific example for the lesson. A phone has a total of three states:
- The phone is switched off — the screen does not respond to being touched
- The phone is on, but the screen is off — the screen only responds to being touched (not swiped) and turns on
- The phone and the screen are on — the response to touches and gestures depends on the active application
Let's model this logic in the class responsible for the screen and add two events to it — touch and swipe:
There are only two events and the same number of conditional constructs. There would be many more events, and they all have to consider the state of the phone and screen activity.
If we solve this problem head-on, we get many conditional constructs in the method for each event. Such code is very complex and fragile. Changing the number of states and adding new events means risking bugs. It's hard to see the whole picture and not miss something.
We can reduce the complexity of such code by two successive transformations: the allocation of the explicit state and the connection of the polymorphism of subtypes.
Explicitly highlighted state
The current implementation of the screen relies on flags. In programming, this is the name given to variables that contain boolean values:
Flags are often (but not always!) a sign of bad architecture. They tend to multiply and overlap. Logic tied to combinations of different flags complicates code analysis:
This style of programming has its name — flag programming. That's what they say about code that's hard to figure out because it has its logic tied to a combination of flags. And having flags will almost certainly lead to that. The issue is that systems usually have more than two states. So, one flag will never be enough.
We can get away from flags by introducing an explicit system state. In our example, it's easy to see that there are only three states:
- Power Off — The power is off (and the screen is off)
- Screen Disabled — The screen is off (but the power is on)
- Screen On — The screen is on
The next step is to replace the flags with a single variable that stores the current state of the system:
The main thing about the code above was that there were no checks for the combination of flags. It doesn't mean we won't have to check several states at once, but system states are much easier to understand than sets of flags.
Classes of States
To get rid of conditional constructs, we need polymorphism. On what basis should we build it? Due to the presence of an explicitly allocated state, it's easy to see the dependence of behavior on it. It's the states that must transform into classes with their state-specific behavior.
The screen, in turn, will get rid of all checks and start interacting with states:
Now the screen does absolutely nothing. Its code initializes the initial state and transfers control to the active state. So what do the state classes look like:
The simplest of all states is the phone's off. In this state, there's no reaction, so the methods are empty. Let's look at ScreenDisabledState:
Touching the screen wakes it up. To do this, ScreenDisabledState must switch to ScreenOnState. That's why we pass the screen to each state. Otherwise, it would be impossible to change it.
And the last state is ScreenOnState. It is the only state in which there is interaction with programs:
Incredibly, there are no more conditional constructs in the code. It's now easy to see the phone's behavior of the phone for all events in a particular state. You need to open the appropriate class. This convenience comes at the cost of needing more files and codes.
It's important not to miss the main idea of the pattern. We introduce state classes just for polymorphism; they do not have any data to work with. Ultimately, all the impact goes to the screen, the entity we simplify.

