Strategy design pattern

As discussed in the previous post, the template pattern has a number of disadvantages due to the fact that it relies on inheritance. The alternative is to use composition over inheritance, that is where the strategy pattern comes in.

With the strategy button, you are programming to an interface. In Ruby we have to concept of duck typing, if an object quacks like a duck then it must be a duck. This means that we don’t need to worry about an objects type. If the object responds to the methods that we expect it to respond to, then we don’t care. In this example we are defining a number of reports that all adhere to the same interface.

Below is the Report class which is responsible for generating a report. Report expects to be initialized with a report_body object, this object should respond to the output method.

class Report
  attr_accessor :report_body
  attr_reader :year, :period

  def initialize(report_body)
    @year = '2016'
    @period = 'q1'
    @report_body = report_body
  end

  def output
    report_body.output(self)
  end
end

The Report class can be initialised by any type of report_body object. Below you can see 3 different report body classes, that all respond to output.

class TechReport
  def output(shared)
    title(shared.year)
    subtitle(shared.period)
    underline
    report
  end

  def title(year)
    puts "Tech Report - #{year}"
  end

  def subtitle(period)
    puts "business period - #{period}"
  end

  def underline
    puts "----------"
  end

  def report
    puts 'Good stuff'
  end
end

class MarketingReport
  def output(shared)
    title(shared.year)
    underline
    report
  end

  def title(year)
    puts "Marketing Report - #{year}"
  end

  def underline
    puts "----------"
  end

  def report
    puts 'Bad month due to weather'
  end
end

class FinanceReport
  def output(shared)
    title(shared.year)
    subtitle(shared.period)
    underline
    report
    extra_info
  end

  def title(year)
    puts "Financial Report - #{year}"
  end

  def subtitle(period)
    puts "business period - #{period}"
  end

  def underline
    puts "----------"
  end

  def report
    puts 'Growing profits in all departments'
  end

  def extra_info
    puts 'Forecast for next month: 32% growth'
  end
end

Finally, here is the result of calling the Report class with each different report_body object

Benefits

The benefit of the strategy pattern is that there is no coupling between subclass and superclass. Report does not check the objects type, which means it can receive an object, proc, lambda or any Ruby construct that implements the correct interface.

Therefore, the strategy pattern is much less coupled than the template pattern. The strategy pattern also gives you more flexibility at runtime.

tech_report = TechReport.new
marketing_report = MarketingReport.new
finance_report = FinanceReport.new

Report.new(tech_report).output
  #=> Tech Report - 2016
      #business period - q1
      #----------
      #Good stuff
Report.new(marketing_report).output
  #=> Marketing Report - 2016
      #----------
      #Bad month due to weather
Report.new(finance_report).output
  #=> Financial Report - 2016
      #business period - q1
      #----------
      #Growing profits in all departments
      #Forecast for next month: 32% growth

Conclusion

The strategy pattern solved the same problem as the template pattern. The difference is that the strategy pattern uses composition of objects, that all implement the same public interface.