In this tutorial, we’ll discuss the State and the Strategy patterns. Often programmers have difficulty understanding the difference between these two patterns. The main goal of this article is to highlight the differences between them and explain their intentions and applications.
2. Strategy Pattern
The primary goal of the Strategy pattern is to create a hierarchy of interchangeable algorithms. The Strategy allows the client to pick and use a particular logic or approach. The following diagram outlines the structure of the Strategy pattern, which includes the Context and interchangeable Strategies:
Suppose we have a collection of users and would like to sort them by their names for our support system and their location for the marketing system. We can identify the sorting logic directly in the client, making the code highly coupled and fragile. Also, we might have issues with adding or removing sorting algorithms.
The better approach is to use the Strategy pattern. To do this, we must identify the comparison logic and pass it to the sorting mechanism. Also, this approach would allow us to change this logic on the fly. This way, we can separate the algorithms, producing better-structured code and making it easier to implement.
The Strategy pattern hides the algorithm implementation from a Client, which simplifies it, and decouples it from Strategies, which sometimes can be complicated and require specific dependencies. Also, this pattern allows extracting the conditional logic from a Client and using polymorphism instead.
One of the problems with this pattern is that sometimes it takes work to create a uniform interface for strategies. Each algorithm might operate on a different data set, and some implementations might end with the methods or the information that isn’t used.
3. State Pattern
We can think about the State pattern as a logical development of the Strategy pattern. There are a couple of prerequisites for it. First, we should have a logical state of an object which affects its behavior. Second, these states change from one to another during the object’s lifetime:
This pattern helps make the distinction between states more explicit and to avoid duplications. ATM is an excellent example of this concept. It can be in different states based on our actions. Although an ATM exposes the same interface, the actions produce different outcomes. One of the main benefits of the State pattern is to externalize the entire state behavior, which makes it easier to change it. Also, this pattern allows adding new states easier.
The State pattern commonly uses two approaches to how it changes the states. The logic can be contained in the Context and orchestrate the states, but it is usually applied only to the system where the state flow is quite simple. Another way is to use choreography and let the states decide on the direction of the flow. This more complex approach may increase the coupling between the states but simultaneously allow the creation of a very intricate system.
The State pattern technically allows us a runtime polymorphism, when it’s possible to change the behavior of the entire object in runtime by switching its state. Also, it simplifies the Context by better organizing state-switching rules. The code will be less complex if the behavior relies on states instead of a set of variables that define different aspects of an object.
4. Strategy vs. State
The UML diagrams for these patterns are almost identical, which creates confusion. However, this happens because the UML doesn’t show the intention and the behavior of the elements in action. Although the structure of the classes is quite similar, these two patterns have different goals and are used in different situations.
Let’s take a car as an example. A car can be in different states. The engine can be on, and the engine can be off. The battery can be dead. The tank can be empty, and so on. In all of these states, the car will behave differently. However, a driver can access the car’s interface: steering wheel, pedals, gears, etc. These are the states, and the entire behavior can be considered a combination of the conditions. All the states would provide a distinct behavior, which is a very convenient way to think about it.
At the same time, the car can have a gas or a diesel engine. This won’t affect the car’s behaviors or states and can be changed at some point. We can install a diesel engine in a gas car if the gas price goes up and we want to save some money. We can consider this as the Strategy we use to implement our car’s inner behavior. However, it’s not a state of a vehicle. It’s not reasonable to assume that a gas engine is a step in the lifetime of a car, and it will at some point switch to another state of being a diesel car.
Thus, the overall implementation of these two patterns is structurally the same. There are different ideas and different goals behind it. The State controls the entire object’s behavior, and it’s reasonable to think that the state will be changed at some point, either by the Context or by the States themselves.
The Strategy helps us to provide a custom behavior to some actions, which might or might not require being changed during the object’s lifetime. Strategies often are set from outside and not aware of each other as they don’t have a natural flow of changes like states have.
In this article, we learned the difference between the Strategy and the State pattern. Even though they have the same idea of changing the behavior of an object, they have different implementations and goals.