How to Use The Strategy Design Pattern in Ruby

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 << "
" html end end end

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 ๐Ÿ™‚

4 thoughts on “How to Use The Strategy Design Pattern in Ruby”

  1. 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โ€ฆ

Comments are closed.