Factory Method パターン
id:hyuki さんのデザインパターン本から Factory Method パターンを移植してみます。
まずは素で移植したものです。
#!/usr/local/bin/ruby class Factory def create(owner) p = self.create_product(owner) self.register_product(p) return p end end class IDCard attr_reader :owner def initialize(owner) puts "#{owner} のカードを作ります。" @owner = owner end def use puts "#{owner} のカードを使います。" end end class IDCardFactory < Factory attr_reader :owners def initialize @owners = Array.new end def create_product(owner) IDCard.new(owner) end def register_product(p) @owners.push(p.owner) end protected :create_product, :register_product end factory = IDCardFactory.new products = [ factory.create("るびお"), factory.create("るびこ"), factory.create("まっつさん") ] products.each do |product| product.use; end factory.owners.each do |owner| puts owner end
書籍では Factory クラスは抽象クラスになっていますが、ここでは普通のクラスとして実装しています。IDCardFactory はその Factory を継承します。
実行すると以下のようになります。
$ ruby factory_method.rb るびお のカードを作ります。 るびこ のカードを作ります。 まっつさん のカードを作ります。 るびお のカードを使います。 るびこ のカードを使います。 まっつさん のカードを使います。 るびお るびこ まっつさん
たまたま目を通した Ruby のモジュールのマニュアルユーザーズガイド に
モジュールはインスタンスを生成しない(抽象クラスであるこ とが保証される).
とあって、モジュールを抽象クラスとして利用することができることが分かりました。そこで Factory クラスをモジュールに変更しました。
#!/usr/local/bin/ruby module Factory def create(owner) ... end end class IDCardFactory include Factory ... end
Ruby っぽくなったし、無理矢理抽象クラスみたいなこともないのですっきりしました。抽象クラスを実現したい場合にはモジュールで、と覚えておきます。
ところで、IDCardFactory という名前が Ruby っぽくないかなと思ったので、IDCard::FactoryFactory::IDCard に変更してみたところ
factory_method.rb:33:in `initialize': wrong number of arguments (1 for 0) (ArgumentError) from factory_method.rb:33:in `create_product' from factory_method.rb:5:in `create' from factory_method.rb:43
とエラーになってしまいました。どうも、IDCard.new
しているところで IDCard クラスではなく Factory::IDCard を対象にしてしまっているような感じです。Foo::Bar は Perl の名前空間と同じようなものだと思っていましたが、ちょっと違うみたいです。スコープのメカニズムか何かが入っているのでしょうか。