Learn about the Strategy Design Pattern in .NET C#
Strategy Design Pattern Definition
Strategy Design Pattern is one of the behavioral design patterns.
Its main goal is to decouple the behavior of an object from its state by modeling the behavior into an abstraction which could have more than one implementation.
Then, this abstracted behavior could be used by the object with the capability of switching between the different implementations of the behavior at run time without the need to re-create the object.
The Strategy Design Pattern is widely used nowadays. Most probably almost every solution you would come across would be using it.
Before the Strategy Design Pattern
Before the existence of the Strategy Design Pattern, the normal way of working was encapsulating everything related to an object inside the object itself. By everything, it is really meant everything.
So, we used to include the state and behavior of an object inside the object. For example, it was 100% logical to have a Person class like this:
However, things got more complicated with the era of game programming.
In game programming, you can’t assume that your game characters would always have the same way of behaving in different situations.
For example, using the same Person example, if your game character is a person and it is modeled using the Person class, then you would face a problem to model the Run behavior of your game character when the character is injured.
What developers used to do then is to create more that one model of the Person class and implement ways of coping the state from one model to another. But this was a nightmare to any developer because multiplying the number of different behaviors you would get a whole bunch of models for only one object.
Another way was to include all parameters in the model itself and start switching the behavior based on these parameters.
For example, following the same Person example, you would add a new boolean property called IsInjured. Then, inside the Run behavior, you would check the value of the IsInjured property and act accordingly.
Still, this is not a good solution as it comes with too many complications. At the end, you would find that your model is bloated with too many details that are actually not controlled or maintained by it.
Applying Strategy Design Pattern
Now, let’s try to apply the Strategy Design Pattern and see if it is going to make our lives easier.
Worth to mention here, we would first apply the pattern on this example as it is described on many online tutorials. Then, I would apply a design enhancement.
Using the same Person example, let’s say that we are building our game and the Person class represents our game character.
First, let’s identify our Person state and behavior separately.
State would be Name and Age.Behavior would be Walk and Run. For simplicity, let’s only consider Walk.
Then, we should abstract the behavior as follows:
And for the different implementations, we have:
And then, the Person class should be modified to start using the strategies as follows:
And the main module should control our character as follows:
Therefore, as you can see, we can now switch between different implementations of IWalkStrategy easily at run time.
However, there is something I don’t like…
Better Design
What I don’t like here is that still our Person class has some behavior which is Walk. Yes, I am aware that it is just a dummy one as at the end the main module decides the actual implementation to be used, but still, it is a behavior.
Is it that bad? Actually no but when your game starts growing you would find that your entities are bloated by too many dummy behavior.
Therefore, I believe that the way we are controlling our Person walking behavior should be different.
This is how I see it should be.
Now Person doesn’t know anything about the Walk behavior. It only holds its own state. Worth to mention here that it is better to make the whole class immutable so that its state is maintained and cannot be tampered with.
And the IWalkStrategy is now applying the behavior on the passed in Person. May be it would be better then to rename the interface to IPersonWalker or something else.
Then the implementations would be modified as follows:
And a new interface would be added as follows:
This interface represents a selector which is capable of selecting the right IWalkStrategy implementation based on the health.
Here we might argue if the SelectWalkStrategy method should expect the health or a Person object as an input parameter. Both would wok but it depends on how complex the logic is.
If you find that the decision would depend on more properties like Age and Health, then it might be good to use Person as the parameter, or even create a new wrapper for the decision criteria.
Then, for the implementation, we can have this:
And for the main module, we would have this:
You can notice that now the Walk behavior is completely decoupled from the Person state.
Furthermore, the IWalkStrategySelector itself could have more that one implementation and we can switch between them easily.
What’s Next
Now you understand what the Strategy Design Pattern is about. However, this is not the end of the story.
You would need to search the internet for more articles and tutorials about the Strategy Design Pattern and its usages. This would help you understand it more.
Finally, I hope you found reading this article as interesting as I found writing it.
Comments