menu
Behavioralarrow_forward_ios
Template Method
assignmentTemplate Method

Description

Template Method is a behavioral design pattern that allows you to define a common abstract scaffold of a base class that can then be overwritten by subclasses.

Problem

Let’s assume that our application has an example table with user data.

| ID | NAME      | EMAIL              |
|----|-----------|--------------------|
| 1  | John Doe  | john.doe@email.com |
|----|-----------|--------------------|
| 2  | Jane Doe  | jane.doe@email.com |
We want to add functionality to it that will allow us to export it to files of various formats, e.g. HTML and JSON.
So let’s create pseudo code that will allow us to do this step by step:
  • create a new file in the specific format
  • format data from the table
  • add them to the file
  • save file
Having written pseudo code, we can create a parent class that will perform the appropriate steps.

class Export
  attr_reader :data, :file

  # Raw data pass as an argument
  def initialize(data)
    @data = data  
  end

  # Abstract steps, without implementation
  def call
    create_file
    format_data
    add_data_to_file
    save
  end

  private

  def create_file
    raise 'Called abstract method: create_file'
  end

  def format_data
    raise 'Called abstract method: format_data'
  end

  def add_data_to_file
    raise 'Called abstract method: add_data_to_file'
  end

  def save
    raise 'Called abstract method: save'
  end

  # Common method that can be used by subclasses
  def timestamps
    Time.new.to_i
  end
end
As seen above, we have defined the specific steps that must be performed to export a file to various formats. Each of these methods should be overridden by the child class to avoid causing an error. The exception here is the timestamp method, which can be used universally by all classes.
So it’s time to create class that include proper implementation for JSON format:

require 'json'

class JsonExport < Export
  def create_file
    @file = File.new("./users_#{timestamps}.json", "w")
  end

  def format_data
    @data = JSON.parse(data)
  end

  def add_data_to_file
    file.write = data  
  end

  def save
    file.close
  end
end
And do the same for HTML:

class HtmlExport < Export
  def create_file
    @file = File.new("./users_#{timestamps}.html", "w")
  end

  def format_data
    # In this case we don't want to format data yet, however
    # we must override this method to avoid error
  end

  def add_data_to_file
    file.write << '<table>'

    data.map do |user|
      file.write << '<tr>'

      # Rest of the implementation that adds users data and
      # creates html table

      file.write << '</tr>'
    end
      
    file.write << '</table>'
  end

  def save
    file.close
  end
end
Now if we prepare data from the table and run our code, we can easily generate code in both JSON and HTML formats.

users = [
  { id: 1, name: 'John Doe', email: 'john.doe@email.com' },
  { id: 2, name: 'Jane Doe', email: 'jane.doe@email.com' }
]

JsonExport.call(users) # => Generate JSON file
HtmlExport.call(users) # => Generate HTML file
So, as you can see, the Template Method allows us to implement abstract code, and then its inheriting classes will be responsible for a specific implementation. In the parent class we can also add common code that will be used by all implementations like timestamps in this example.
There is also nothing stopping us from creating more implementations in the future, including other formats. e.g. TXT or CSV.