The State as a Term
At its necessary, we can say that the state can be defined as the condition of something changeable. Early in our education years, we learned standard states of matter — solid, liquid, and gas. We learned that each state has different properties and behaviors.
As software developers, we must take care of the different states of the applications. With more states comes more code to tame them. Without proper design, we can quickly end up in the big world of decision-making statements.
Boolean Flag Pattern
Boolean Flag Pattern is not the official name of the pattern, but I think that a more significant half of you recognize a meaning. When the developer finds out that he can or can’t do something in a particular state, he will introduce a boolean flag and use the decision-making statement.
In the example, the private field
sleep is a boolean flag. The boolean flag is the most common solution of state control in software development. With more flags, the code begins to smell funny. Complexity is growing. It can appear more and more bugs in the system.
Struggles with complexity inspired a group of programmers in the nineties to came up with State Design Patterns in the book titled Design Patterns: Elements of Reusable Object-Oriented Software.
State Design Challenges
The pattern was developed to overcome two primary design challenges:
- How can an object change its behavior when its internal state changes?
- How can state-specific behaviors be defined in a way that states can be added without altering the behaviors of existing states?
Boolean Flag Pattern can answer the first challenge. The object will know when to change its behavior because we detect internal changes with boolean flags. You can use boolean flags and decision-making statements to take control of object states.
The second can be answered with the Boolean Flag Pattern too. In this case, state-specific behaviors are code segments of the decision-making statement body. You will not alter this code segment. You will add a new branch of the
But this solution brings more complex code. More states mean boolean flags. More boolean flags indicate more decision-making statements, which means more debug time while fixing the bug, which means headache, which means you will need a booster which leads to alcoholism.
State Design Pattern
State Pattern is part of behavior patterns designed to avoid overusing decision-making statements by applying the bright use of object-oriented languages.
The pattern consists of three components:
- Abstract State
- Concrete State
In the diagram above, you can see a representation of Context(DogContext), Abstract State(IDogState), and Concrete States(SleepState and BarkState).
The Context contains a method
TransitionToState, and this is the only allowed way to change the transition of the Dog object. The method invokes the
EnterState method of its parameter, the object of an Abstract State(IDogState). Also, it introduces a value of parameter into Context’s property
EnterState of Concrete States will do state-specific behavior.
Sleep are triggers to change state from one to another if it is allowed. For example, if the dog can change state from bark into sleep but not the other way around, implementation will look like this.
Pros and Cons
Code following State Design Pattern is:
- More modular.
- Easier to maintain.
- Less challenging to debug.
- More extensible.
As a disadvantage can be mentioned:
- It takes time to set up.
- There are more classes, more moving parts.
- It can be less performant.
Thanks to the excellent fellow developer Paul Johnson you can take advantage of the NuGet package named Stateless. It is a lightweight state machine implementation with fluent code style and can save you a lot of time. You can get started at the package GitHub page. Below you can see a little teaser of usage.
State Machine is controlled by two components, mostly enums. One is representing the state, and the other one is representing the trigger for state transition.
Once you make yourself comfortable with a Stateless package, the disadvantages mentioned above will disappear.
I created a sample project on my GitHub page. It is the .NET Core 3.1 WPF application that follows the MVVM approach.
The project consists of three main folders. Each folder contains a different design of state managing.
In the first folder is state managed by using the Boolean Flag Pattern. The second one holds the code of State Design Pattern, similar to our dog example. The third operates with a Stateless package.
The application simulates order states. You will find four of them; a new, create, cancel, and shipped state.
The New State is a default state, and it represents a new order before creating one. While in the New State, you can only create an order.
Once you are in the Create State, you can proceed to cancel or ship the order.
A canceled or shipped order is a last in life-cycle of order, and you can choose between resetting state to default New State or immediately create another order.