The first step in learning this pattern is defining a problem and solving it by using a strategy pattern. Imagine that we are developing a game “Street Fighter”. For convenience, let’s suppose that a character has four different moves. They are - kick, punch, roll and jump. For each character kick and punch moves are compulsory, but roll and jump are additional. How should your classes be modeled? Considering that you use inheritance and abstract out the common features in a Fighter class and let other characters subclass Fighter class.
Fighter class will have default implementation of regular actions. Any character with a specific move can override that action in its subclass. You can see this on Class diagram below:
Which problems does the above design have?
For example, character doesn’t perform jump move. It still inherits the jump behavior from the main class. In that event you can simply override jump, but you may have to do such operations for all existing classes and furthermore, keep in your mind that future classes should be worried about that too. So inheritance is not the appropriate principle here.
What about an Interface?
Look at the design below:
It’s pretty well-ordered. Actions, which some characters might not perform, were removed out of Fighterclass and interfaces were created for them instead. It means that only characters that are supposed to jump will implement the JumpBehavior.
Which problems does the above design have?
Code reuse is the main problem with this kind of design. We may duplicate code, because there is no default implementation of jump and roll behavior. In that way we may need to rewrite the same jump behavior again and again in many subclasses.
How to avoid this?
At first sight, the most efficient way is to use multiple inheritance and create JumpBehavior and RollBehavior classes instead of interface. But due to the fact, that multiple inheritance is not supported in many languages and has too many problems with it, we cannot use it.
Here strategy pattern comes to help us. We need to learn what the strategy pattern is and then apply it to solve our problem.
Definition:
Wikipedia defines strategy pattern as:
“In computer programming, the strategy pattern (also known as the policy pattern) is a software design pattern that enables an algorithm’s behavior to be selected at runtime. The strategy pattern:
· defines a family of algorithms,
· encapsulates each algorithm,
· makes the algorithms interchangeable within that family.”
Class scheme:
Instead of inheritance for reuse we focus on composition here. Context is made up of a Strategy. The Context delegates a behavior to Strategy instead of implementing it. The context class will require the behavior change, which can be dynamic. So we can change behavior without affecting our context, because Strategy is implemented as an interface.
To have a deeper understanding of strategy pattern we should use it in practice to solve our problem.
Advantages:
- We can define a family of algorithms as a class hierarchy, which is interchangeable to alter application behavior and can use it without modifying its architecture.
- We can easily introduce new algorithms complying with the same interface by encapsulating the algorithm individually.
- Strategies can be switched at run-time.
- We have an opportunity to choose the required algorithm, without using a “switch” statement or a series of “if-else” statements.
- We can change the implementation of an algorithm without affecting the Context class because Data structures used for implementing the algorithm are completely encapsulated in Strategy classes.
Disadvantages:
- The right strategy for the right situation could be selected only if the application includes all the strategies.
- Normally, communication between Context and the Strategy classes is carried out through the interface established by the abstract Strategy base class. Some concrete Strategy classes might not implement all the required behaviors, although Strategy base class must provide interface for them.
- The Context can be configured with the required Strategy object in most cases. Therefore, two objects instead of one need to be built and supported.
Then we will apply the strategy pattern to the problems with fighter and talk about the implementation.
The main thing is the identification of behaviors that may differ from class to class after time and then their separation from the others. For example, let that behaviors be kicks and jumps. For their separation, we will extract both methods from the Fighter class and design a new set of classes to show each behavior.
Now, kick and jump behavior will be delegated to the Fighter class, instead of using the kick and jump methods defined in the Fighter class or its subclass.
After this modification, the final class diagram will look like (Click the image for a better view):
In comparison with the definition of a strategy pattern and our design, encapsulated kick and jump behaviors are two families of algorithms. And these algorithms can be replaced with each other, as can be seen from the implementation.
Top comments (1)
Super. Thank you!