アルゴリズムを実行時に選択することができるデザインパターンである。
使い道
- Template Method パターン と同じ
- ただし、Template Method パターン の時に書いたような定形のプロトコルに沿った実装ではなく、個別に柔軟な実装を書きたい時に使う感じか
実装
StringLister#display の挙動を、Strategy のサブクラスに移譲している
これにより、コンストラクタの引数で、StringLister#display の挙動が柔軟に変えられるようになる
strategy.rb
class StringLister
attr_reader :items
def initialize(items, strategy)
@items = items
@strategy = strategy
end
def display
@strategy.display(@items)
end
end
class Strategy
def display(items)
raise NotImplementedError
end
end
class TextStrategy
def display(items)
items.join("\n")
end
end
class HtmlStrategy
def display(items)
result = []
result << "<html><body>"
items.each{|item| result << "<div>#{item}</div>" }
result << "</body></html>"
result.join("\n")
end
end
items = %w[abc def ghi]
puts StringLister.new(items, HtmlStrategy.new).display
# <html><body>
# <div>abc</div>
# <div>def</div>
# <div>ghi</div>
# </body></html>
puts StringLister.new(items, TextStrategy.new).display
# abc
# def
# ghi
より、Ruby 風な書き方
StringLister のコンストラクタに、Strategy のサブクラスではなく、block を渡す
こっちの方が、いちいちクラスを定義する必要がなく、より柔軟な書き方ができる
strategy.rb
class StringLister
attr_reader :items
def initialize(items, &strategy)
@items = items
@strategy = strategy
end
def display
@strategy.call(@items)
end
end
items = %w[abc def ghi]
disp = StringLister.new(items) do |items|
result = []
result << "<html><body>"
items.each{|item| result << "<div>#{item}</div>" }
result << "</body></html>"
result.join("\n")
end.display
puts disp
# <html><body>
# <div>abc</div>
# <div>def</div>
# <div>ghi</div>
# </body></html>
disp = StringLister.new(items) do |items|
items.join("\n")
end.display
puts disp
# abc
# def
# ghi
雑感
多分だけど、関数をオブジェクトにしたいが為のパターンなのかな?
- C++ は、関数が第一級オブジェクトでない