Pluggable Selector
今回はPluggable Selector。
文字から見てplug + ableなので、プラグを差し替えする感覚で振る舞いを変えれる、みたいなイメージだと思う。
本では、
インスタンス固有の振る舞いを簡単にコーディングするにはどうすればいいの
と始まっている。
次のコードのような感じでインスタンス固有の振る舞いを実装しようとすると、
振る舞いの数だけクラスが必要になってしまっている。
class Translator def initialize(text_ja: "") @text_ja = text_ja translation end def say @text end end class TranslatorEn < Translator def translation # 翻訳APIを叩たいて @textに設定する end end class TranslatorIt < Translator def translation # 翻訳APIを叩たいて @textに設定する end end class TranslatorCy < Translator def translation # 翻訳APIを叩たいて @textに設定する end end TranslatorEn.new(text_ja: 'こんにちは世界').say # hello world TranslatorIt.new(text_ja: 'こんにちは世界').say # ciao mondo TranslatorCy.new(text_ja: 'こんにちは世界').say # helo byd
解決方法は、メソッド(メッセージ)をインスタンスに保持しよう、とのこと。
class Translator def initialize(lang: lang) @lang = lang end def say(obj) obj.send(@lang) end end class Word attr_accessor :en, :it, :cy def initialize(text_ja: nil) @text_ja = text_ja translation_from_japanese end def translation_from_japanese # 翻訳APIを叩いて @en、 @it、 @cyに翻訳された値が設定される end end ohayo_message = Word.new(text_ja: 'こんにちは世界') john = Translator.new(lang: 'en') # 英語 itaria = Translator.new(lang: 'it') # イタリア語 cocoa = Translator.new(lang: 'cy') # ウェールズ語 john.say(ohayo_message) # hello world itaria.say(ohayo_message) # ciao mondo cocoa.say(ohayo_message) # helo byd
このパターンを使うメリットは、サブクラスを作る必要がなくなること。
一方で可読性下がっている感。
いまいち腑に落ちていないけどインスタンス固有の振る舞いを定義したくなったら思い出したい。