format_list_bulletedState

Description

Strategy is a behavioral design pattern that allows you to define a common algorithm and then put its implementations into separate classes.

Problem

Strategy pattern can sometimes be used interchangeably with the Template Method pattern. They often solve a similar problem in a different way.
Let’s assume that our application has an example table with users 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 or TXT.
In the first step, we will define our main class, which will be responsible for export.

class Export
  # Formatter will be used to define the implementation
  attr_reader :data, :formatter

  def initialize(data:, formatter:)
    @data = data
    @formatter = formatter.new(data)
  end

  # Method that export data in specific format
  def call
    formatter.call
  end
end
Then we have to create a second class that will include specific implementation for a specific format. In this example JSON.

require 'json'

class JsonExport
  attr_reader :data

  def initialize(data)
    @data = data
  end

  def call
    create_file
    format_data
    add_data_to_file
    save
  end

  private

  def create_file
    @file = File.new("./data.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
It’s quite similar to a Template Method, but now we don’t have to keep a strictly defined algorithm because each class can create its own unique implementation. So let’s create implementations for a plain text file.

class TxtExport
  attr_reader :data

  def initialize(data)
    @data = data
  end

  def call
    File.write('./data.txt', data.to_s)
  end
end
Now if we prepare data from the table and run our code, we can easily generate code in both JSON and TXT formats.

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

Export.new(data: users, formatter: JsonExport) # => Generate JSON file
Export.new(data: users, formatter: TxtExport) # => Generate TXT file
The whole power of this pattern is that we create a main class that is responsible for main action, and specific implementations of this action are passed to other classes.