A lot has been written on this topic, and some pretty eloquent, then why another article. I feel most of these are too abstract for a beginner, and he/she is pretty much entangled in the definition and problem statement rather than truly comprehending the true power and possibilities of usage. This is my humble effort that is targeting not much experienced programmers rather than seasoned ones; some may get the benefit though. I will try to keep things as simple as possible. I have chosen the strategy pattern as the first pattern to write about as it’s one I come across most, and I think it’s among the most widely used design patterns. The class diagram for the strategy pattern is given below.
First a formal definition:
Strategy pattern is a technique of decoupling an algorithm from a class that is hosting it so that we can choose a particular algorithm at runtime. Our host (ie context) class doesn’t care what the concrete implementation is any more as it will deal with the abstraction.
It’s alright, if at this stage, you don’t understand even a word of it. I hope that after reading the next section, if you will read this definition again it will not only make sense, but you will also understand where to apply this pattern. However, I am presuming that the reader is well acquainted with object-oriented terms like polymorphism, inheritance, interfaces and abstract classes.
The Thought Process
Consider this scenario that you are developing a reusable Electronic Medical Record API and your current task is to implement the module that will calculate Body Surface Area (BSA) that is based on two factors, height and weight, residing in the class
Check http://www.halls.md/body-surface-area/refs.htm for different formulae available. Now the hypothetical part. Let’s say currently only two formulae are known, Boyd and Heycock. But there are some under standardization or in the development phase. A possible modification to the
VitalSigns is as below:
At first sight the solution seems fine, but it has one flaw, it’s not extensible. When the new formulae will be standardized, API users will need support for these in their applications. With this implementation, the only possibility you have is to add another case in the function,
CalculateBSA(), and then recompile your API and provide them the patch. To worsen the situation, your DLL might be strongly named causing the developers to recompile everything. Or the API user will have to implement most from scratch, and they won't be getting any benefit from your API.
How nice it would be if you can somehow manage this changing functionality separate of your
VitalSigns class and provide them as dependencies when user needs BSA Calculation. You had better provide the user some mechanism to plug their implementations through this mechanism, it will be extremely flexible. That’s where strategy pattern comes into play. It will make your solution quite extensible, and your
VitalSigns class will need no change for new implementations.
So now we will see how strategy pattern will solve this problem. The problematic function is
CalculateBSA. Hence we will implement it separately. First, we will implement the interface (because it is the key to extensibility, and by the end you will understand why).
So here is the interface you developed.
NOTE: Usually we pass the whole context object (
VitalSigns instance in our case). The debate for choosing scalar values like this or whole context is pretty similar to choosing Scalar values or entity classes across different layers. I will come to this point at the end, so for now you can pretty much ignore this note .
Each formula you have will become a concrete implementation of this interface, e.g. for boyd.
Since all concrete implementations are derived from
IBsaCalculator, all these can be treated as
IBsaCalculator itself. To accommodate our changes, we will update our vital signs class as below:
CalculateBSA(), we have called the
CalculateBSA(double, double) of interface, which will obviously be called polymorphically.
What Have We Accomplished
Well this solution is pretty much extensible. Let me clarify this with an example. Suppose when you deliver your API, the very next day Dubois formula was released. Now the user does not have to wait for a new version, and can implement it in his application, by deriving from
IBsaCalculator and then assigning it to
BsaCalculator property. All he has to do is to give another concrete implementation of
We don’t have to modify the
VitalSigns class for new implementations and we can plug different implementations of calculators at runtime according to the requirements.
Who Will Provide the Dependency Then?
There is no such thing as absolute decoupling. Dependencies are like matter, that cannot be destroyed. Seasoned programmers just mold them to get better extendible and maintainable solutions. For example when you use frameworks like Spring, dependencies just don’t vanish in thin air. You’re mentioning them in XML files rather than hard coding in your code, and now rather than your code, the framework is providing these dependencies, making your classes decoupled from each other. The question is where should we provide our concrete implementations to
VitalSigns class? Obviously now, the client (by client, I mean the code that is going to use all this) has to know about these concrete implementations and he is responsible for providing the required calculator. We can delegate the creation task to factory method to add another layer of abstraction, but let’s not overcomplicate things and stay with a simplistic implementation. Let me explain this point. Suppose we are displaying all available formulae in a
The event handler code for user selection will look something like this:
vs is the instance of our context class (
So we have seen how strategy pattern has made our
VitalSigns class extendible. We can not only plug in our own implementation, but also can provide as well as change the choice at runtime.
Another Possible Scenario
I will not spend much time on this scenario, and leave it as an exercise to the reader. Suppose you have a huge hierarchy of classes, for example, something like world famous
Vehicle class, which has derivatives like car, truck, wagon, etc. Each derivation has many further derivations and so on. Suppose you have to override
Move() function in most classes in this hierarchy. This will make your implementations of
Move() cluttered across the whole hierarchy, making it very hard to maintain. So you can use the strategy pattern, and separate out the Move implementation and implement it isolatedly. Here we haven’t mainly achieved extensibility but maintainability.
Scalar Values or Context Class Itself?
In my implementation I have used scalar values,
Usually people prefer passing the whole context like
Both have their pros and cons. Scalar will make this implementation totally decoupled from
VitalSigns which is beneficial in some cases, but if in future some formula is developed that is consuming other
VitalSigns property, e.g. temperature additionally, it will break the implementation. Using context class will make it coupled with
VitalSigns but will abort those sort of changes. It’s a matter of choice and depends on your requirement.
About this Article
This article and the next ones in this series are by no means a comprehensive guide to design patterns. These will be mostly targeting beginners, explaining to them that design patterns are not some sort of rocket science, but just simple and elegant solution that will save you from future refactoring and maintenance. The goal is to present these patterns as simple as possible and refraining from any complex implementations. Focusing on just the pattern at hand, so a newcomer won't be confused. By the end of the series, I will try writing about how to network these patterns to create a flexible and maintainable application framework. For example, we could have created a Factory to instantiate and decouple the clients from creation of concrete implementations, but rather than presenting it here, I will try explaining it in a Factory pattern tutorial so the reader will not get confused. Your feedback will be very important, telling me the weaknesses of this article, and where I failed to clarify the concepts. Thanks.
About the Code Provided
The code I have attached with this article is developed in C# using Visual Studio 2008. However if needed, I can provide the implementation for Java using Eclipse or NetBeans, or even C++ version.
- 19th March, 2010: Initial post