Let’s talk about the strategy design pattern!
This design pattern helps you change the main algorithm inside a method.
You do this by passing in a class that implements this algorithm, instead of hardcoding it into the class.
And when I say algorithm I don’t mean a fancy computer science algorithm, but any code that follows a sequence of steps to get a result.
When To Use The Strategy Pattern
What problem is this pattern solving exactly?
The open/closed principle problem.
This principle says that:
“Software entities (classes, modules, methods) should be open for extension & closed for modification.”
This means that to make a class do new things you shouldn’t need to change the class itself. This makes your code more flexible & robust at the same time.
But how do you accomplish that?
Using design patterns like the strategy pattern ๐
Let’s say you have a ReportGenerator
class, and you want to generate many kinds of report formats using the same data.
You could write a method for each report type, but that would mean you have to change the class (by adding a new method) every time you want to add a new type, breaking the open/close principle.
Or even worse!
You could have a single HUGE method with all sorts of if statements… These often don’t end up well.
But what if the class didn’t have to know how to format the reports?
What if the algorithm came from outside the class?
Then we can change the formatting algorithm whenever we want without changing the class itself.
This solves the problem.
And that’s exactly what the strategy pattern is all about.
Strategy Pattern Example
A strategy is implemented in the form of a class with a single method.
Here are some formatting strategies for the ReportGenerator
class:
require 'json' module ReportFormatters class JSON def self.format(data) data.to_json end end class PlainText def self.format(data) data.to_s end end class HTML def self.format(data) html = "" html << "
- "
data.each { |product, amount| html << "
- #{product}: #{amount} " } html << "
Notice how all of the strategies implement the format
method. This method will be called by the report generator class.
Here is the code:
class ReportGenerator def self.generate(data, formatter) formatter.format(data) end end data = { onions: 31, potatoes: 24, eggs: 10 } p ReportGenerator.generate(data, ReportFormatters::HTML) p ReportGenerator.generate(data, ReportFormatters::JSON) p ReportGenerator.generate(data, ReportFormatters::PlainText)
Now:
If you want to change the output format (from HTML
to PlainText
) you just have to pass in a different strategy & it will work as long as the strategy implements the format
method.
Another way to think about this pattern is that you are hiring a consultant specialized in a specific business strategy, the consultant will come in & implement the strategy for you, then this strategy will produce a specific result.
In contrast, a pattern like the decorator pattern is like adding new ornaments to a Christmas tree to make it more flashy.
You can only work with 1 strategy at a time, but you can have as many ornaments as you want.
Video
[responsive_video type=’youtube’ hide_related=’0′ hide_logo=’0′ hide_controls=’0′ hide_title=’0′ hide_fullscreen=’0′ autoplay=’0′]https://www.youtube.com/watch?v=zLgEtOl3eGQ[/responsive_video]
Summary
You have learned about the strategy design pattern, use it when you need to change part of what your class does without having to change the class itself.
Thanks for reading!
Don’t forget to share this post on Twitter if you found it interesting, it will help more people enjoy it too ๐
Thanks man, your description was very helpful! At last I was able to overcome laziness and grasp another pattern ๐ Now it feels a good portion of my code needs refactoringโฆ
Thanks for your comment Sergey! I’m glad you found this to be helpful ๐
Thank Jesus, for this nice article. I’ll remember this before adding more methods to my classes.
Thanks for reading! ๐