Today, I’m going to write about the strategy design pattern and give some examples on where it should be used and why it’s useful. I’m really sleepy, only had 3 hours of sleep, so I hope I can do this quick and off the top of my head.
The Problem and Design Smell
Suppose we have the following class hierarchy:
We have an abstract Animal class, that has two virtual methods
MakeNoise(). Both household
Cat and household
Dog classes inherit from
Animal, along with inheriting behavior animal behavior. Inheriting
behavior typically leads to bad design and maintainability. Let’s look
at the code implementing the classes:
When we run these classes, we get the following output:
The problem (‘design smell’) associated with this approach is simply the
way we are placing the implementation of the actions
MakeNoise() inside the concrete classes. This doesn’t exactly lead to a
maintainable design. For example, suppose we want to extend our animal
kingdom by introducing an
AllyCat class. An
AllyCat might have different
Eat() (eating out of the trash), but still have the
same kind of
MakeNoise() (moew) implementation. You might think, “well,
we can just derive from the concrete class
Cat, and override the
method with our own
AllyCat.Eat() implementation” and you’re right, you
can. However, extending behavior through hierarchical inheritance should
be avoided. Not only that, designs like this can lead to duplicate code.
Instead, you should compose classes with isolated algorithm implementations. The goal is to separate varying-code from non-varying code. A quick way to identify non-varying code aspects of your classes is through “has-a” relationships. A Dog “has-a” distinct eating behavior, a Cat “has-a” distinct eating behavior, an AllyCat “has-a” distinct eating behavior, so on and so forth… From our classes and implementation, we have noise behaviors and eating behaviors.
The solution to basic problems is to implement a Strategy pattern. Consider the following architecture:
And taking a peek in some of the Cat, Ally Cat and Dog code:
All that we have done with this new architecture is delegate the “making noise” and “eating” to behavioral classes that specifically handle the task. We have delegated control to the behavioral classes. This is a much better extensible and coherent design and often leads itself to better to code reuse.
Whenever we create classes that are composed of behavioral actions, we call these compositions. Classes such as Dog, Cat, and AllyCat are compositions of behaviors and gain their behavior through composition, not inheritance. Inheritance (like the previous architecture) can lead to unintended consequences when fiddling with the superclass and is more prone to breaking existing code.
- Algorithm Families – A general case different algorithms need to run depending on a particular situation.
- Sorting - Used in sorting when different types of sort algorithms need to be applied to a collection. For example,
- Behavioral – Used when many distinct “behaviors” are expected of derived classes.
Naming Keywords for the pattern
When naming your interfaces, here are some keywords to include in the naming of your strategy:
- General: Strategy, Algorithm
- Example: CaliforniaTaxStrategy
- Behavioral: Behavior, Action, Style
- Example: BarkBehavior
Principals to take away from this strategy
Look at your application, and analyze the classes that are producing the Design Smell. Take those classes, and analyze the functionality of the classes that are producing this type of Design Smell. Create a separation of varying code and non-varying code (such as specific behaviors that tend to stay the same across all classes or with every new requirement). Once you’ve identified the non-varying code, then you have an opportunity to extract the behavior from the class and isolate it in its own behavioral family.
Use composition rather than inheritance. Composition is encapsulating several behavior/strategies in one class to perform its work. Classes should not inherit their behaviors from base classes; instead they should be composed of behaviors/strategies.
Has-A relationships are good indicators that strategy pattern can be used.
Formal definition of the strategy pattern
The Strategy Pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable. Strategy lets the algorithm vary independently from clients that use it.
This blog post is a little dry because I’m really sleepy, I’ll try updating it to make it a little more clear later when I have more time. But I hope some of you understand! Later I’ll show you how to improve this architecture.
Share onTwitter Facebook Google+ LinkedIn
Leave a comment
Your email address will not be published. Required fields are marked *