Template design pattern

I’m currently reading Design Patterns in Ruby by Russ Olsen, which is a book about implementing some of the Gang of Four’s design patterns in Ruby. I shall be summarising what I’ve read in a series of posts and providing some of my own examples. The purpose is mainly to improve my own understanding but I hope these blog posts are useful for some people. If you find these posts interesting then I highly recommend that you purchase a copy of Russ’s book.

Template Pattern

The template pattern allows you to set up the outline of an algorithm and then modify that algorithm by overriding parts of it. The template design pattern is based on inheritance and it works by defining an abstract class, which outlines the algorithm. Subclassing the abstract class allows you to customise parts of the algorithm.

In this example I want to generate reports for different departments within a company, that have the same look and feel. The first step is to define an abstract class:

class BaseReport
  # Template algorithm
  def self.output
    report_title
    underline
    after_title
    report
    after_report
  end

  # Part of the algorithm that should be overridden by subclass
  def self.report_title
    raise NotImplementedError, 'Please specify a report title'
  end

  # Part of the algorithm that should be overridden by subclass
  def self.report
    raise NotImplementedError, 'Please define a report'
  end

  # After title hook
  def self.after_title
  end

  # After report hook
  def self.after_report
  end

  private

  def self.underline
    puts "----------"
  end
end

The outline method defines the algorithm. At this point I need to introduce two new concepts; abstract methods and hooks.

Abstract methods

As we know, Ruby does not have true abstract classes but we can replicate the idea of abstract classes by raising errors in the methods of the base class. In the example above, report and report_title are both abstract methods. They raise an error if they are not implemented in the subclass. By defining abstract methods you are forcing subclasses to implement their own logic for that part of the algorithm. So, in this example we are forcing subclasses to define their own report title and report contents.

Hooks

Hooks are a way of allowing subclasses to add behaviour to an algorithm but this is purely optional. In the example above, after_title and after_hook are hook methods. These methods allow subclasses to add behaviour to the report generating algorithm either after the title or after the report.

If the hook methods are not overridden by the subclass then the subclass inherits the implementation from the super class – which does nothing.

Subclasses

After defining the abstract class, you are in a position to create the subclasses. In this example I’m going to create 3 different subclasses for the tech department, marketing department and finance department. Each of the subclasses must inherit from BaseReport and must implement report_title and report methods. TechReport and FinanceReport both additionally implement some of the hook methods.

class TechReport < BaseReport
  def self.report_title
    puts 'Tech Report'
  end

  def self.after_title
    puts 'In addition the the usual report we will have an update on tech debt'
  end

  def self.report
    puts 'Good stuff'
  end

  def self.after_report
    puts 'Tech debt has been reduced due to the use of the template pattern'
  end
end

class MarketingReport < BaseReport
  def self.report_title
    puts 'Marketing Report'
  end

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

class FinanceReport < BaseReport
  def self.report_title
    puts 'Financial Report'
  end

  def self.report
    puts 'Growing profits in all departments'
  end

  def self.after_report
    puts 'Forecast for next month: 32% growth'
  end
end

As you can see, each subclass is able to implement it’s own specific behaviour whilst utilising the algorithm defined in the abstract class.

Finally, here is the result of our efforts:

TechReport.output
  #=> Tech Report
      #----------
      #In addition the the usual report we will have an update on tech debt
      #Good stuff
      #Tech debt has been reduced due to the use of the template pattern
MarketingReport.output
  #=> Marketing Report
      #----------
      #Bad month due to weather
FinanceReport.output
  #=> Financial Report
      #----------
      #Growing profits in all departments
      #Forecast for next month: 32% growth

Drawbacks

The main drawback of the template pattern, as far as I can see, is that it makes use of inheritance. Ruby developers tend not to use inheritance much and the gang of four authors themselves favour composition over inheritance.

The problem with inheritance is that your classes are tightly coupled to the parent class, as a posed to duck typing where the class only needs to implement the same interface. The other problem with inheritance is, whenever you want to add a new algorithm, you need to create a new subclass. With composition, you only need to create a new object that implements the interface.

Conclusion

The template method is a useful design pattern that allows you to define a template (abstract) algorithm and create numerous subclasses that vary the algorithm for their own specific need. The downside of this design pattern is that Ruby programmers tend to prefer composition over inheritance. My next post is all about the strategy pattern which achieves the same results as the template design pattern, using composition rather than inheritance.