「Rubyによるデザインパターン」の読書メモです。
時々、個人の見解入りです。
継承に基づいた設計は、いくつかの深刻な不利益をもたらします。たとえどんなに注意深くコードを設計しても、サブクラスはそのスーパークラスに依存してしまいます。
そこで、「委譲」を使ってコードを書き直してみます。
Strategy を適用する (version1)
class Formatter
def output_report(title, text)
raise 'Abstract method called'
end
end
class HTMLFormatter < Formatter
def output_report(title, text)
puts '<html>'
puts "<head><title>#{@title}</title></head>"
puts('<body>')
text.each do |line|
puts("<p>#{line}</p>")
end
puts('</body>')
puts '</html>'
end
end
class PlainTextFormatter < Formatter
def output_report(title, text)
puts("*****#{title}*****")
text.each do |line|
puts(line)
end
end
end
class Report
attr_reader :title, :text
attr_accessor :formatter
def initialize(formatter)
@title = '月次報告'
@text = ['順調', '最高']
@formatter = formatter
end
def output_report
@formatter.output_report(@title, @text)
end
end
report = Report.new(HTMLFormatter.new)
report.output_report
report.formatter = PlainTextFormatter.new
report.output_report
formatter を外から注入しているので、依存性の注入と言えそうです。
コンテキストとストラテジの間でのデータを共有する方法
- コンテキストがストラテジオブジェクトのメソッドを呼び出すときに引数で渡す
- ストラテジへと渡されたコンテキスト自身の参照を使う
- データの流れが単純化されますが、コンテキストとストラテジ間の結合度を上げてしまうことにもなります。
Strategy を適用する (version2)
Formatter クラスはインターフェースを規定するためにしか、存在しません。
Ruby のダックタイピングの哲学に反するため、削除します。
class HTMLFormatter
def output_report(title, text)
puts '<html>'
puts "<head><title>#{@title}</title></head>"
puts('<body>')
text.each do |line|
puts("<p>#{line}</p>")
end
puts('</body>')
puts '</html>'
end
end
class PlainTextFormatter
def output_report(title, text)
puts("*****#{title}*****")
text.each do |line|
puts(line)
end
end
end
class Report
attr_reader :title, :text
attr_accessor :formatter
def initialize(formatter)
@title = '月次報告'
@text = ['順調', '最高']
@formatter = formatter
end
def output_report
@formatter.output_report(@title, @text)
end
end
report = Report.new(HTMLFormatter.new)
report.output_report
report.formatter = PlainTextFormatter.new
report.output_report