Паттерны проектирования в Ruby: Стратегия (Strategy)

6 ноября 2015, пятница

Рассмотрим еще один поведенческий шаблон проектирования, который назвается Стратегия. Суть паттерна заключается в делегировании какого-то поведения соответсвующему классу, реализующему определенный алгоритм. Например, у нас есть какой-то отчет, и в зависимости от запроса надо будет выводить информацию в HTML или XML, или как простой текст.

Итак, класс Report

class Report

  attr_reader :title, :text
  attr_accessor :formatter

  def initialize(formatter)
    @title = 'Monthly Report'
    @text = ['Things are going', 'really, really well.']
    @formatter = formatter
  end

  def output_report
    @formatter.output_report(self)
  end

end

Объект класса Report будет обладать свойством formatter, которое будет являться объектом, реализующим необходимый алгоритм вывода отчета.

Для этого создадим классы, реализующие необходимые варианты вывода отчета. Это будут классы HTMLFormatter, XmlFormatter и PlainTextFormatter, которые унаследованы от базового класса Formatter и должны реализовыать метод output_report, в который в качестве параметра должен будет передаваться наш отчет.

class Formatter

  def output_report(title, text)
    raise 'Abstract method called'
  end

end

class HTMLFormatter < Formatter

  def output_report(context)
    p('<html>')
    p(' <head>')
    p(" <title>#{context.title}</title>")
    p(' </head>')
    p(' <body>')
    context.text.each do |line|
      p(" <p>#{line}</p>")
    end
    p(' </body>')
    p('</html>')
  end

end

class XmlFormatter < Formatter

  def output_report(context)
    p('<?xml version="1.0" encoding="UTF-8"?>')
    p('<note>')
    p("<title>#{context.title}<title>")
    p("<body>#{context.text.join(' ')}<body>")
    p('</note>')
  end

end

class PlainTextFormatter < Formatter

  def output_report(context)
    p("***** #{context.title} *****")
    context.text.each do |line|
      p(line)
    end
  end

end

Результат:

report = Report.new(HTMLFormatter.new)
report.output_report
=begin
"<html>"
" <head>"
" <title>Monthly Report</title>"
" </head>"
" <body>"
" <p>Things are going</p>"
" <p>really, really well.</p>"
" </body>"
"</html>"
=end

report.formatter = PlainTextFormatter.new
report.output_report
=begin
"***** Monthly Report *****"
"Things are going"
"really, really well."
=end

report.formatter = XmlFormatter.new
report.output_report
=begin
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
"<note>"
"<title>Monthly Report<title>"
"<body>Things are going really, really well.<body>"
"</note>"
=end