Factory Method パターン(特異クラス編)
http://d.hatena.ne.jp/rubyo/20060405/1144211265より。
ところで、IDCardFactory という名前が Ruby っぽくないかなと思ったので、IDCard::Factory に変更してみたところ
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 の名前空間と同じようなものだと思っていましたが、ちょっと違うみたいです。スコープのメカニズムか何かが入っているのでしょうか。
IDCard::Factoryとするのであれば、IDCardのクラスの定義の中にFactoryクラスの定義を書きましょう。エラーの内容はFactory::IDCardと書いてあったりIDCard::Factoryと書いてあったりするのでよく分かりませんが推測すると、Factory::IDCardと定義しているのであれば、Factory::IDCard内でトップレベルのIDCardクラスを呼び出すのなら::IDCard.newとトップレベルであることを明示的に指定する必要があります。
ただこの場合は、入れ子になったクラスを作成するよりはIDCardFactoryクラスを定義してやってそれにFactoryモジュールをMix-inする方が個人的にはRubyっぽいような気がします。
あるいはFactoryをモジュールではなくクラスとして定義して、必要なFactoryに対しては特異クラスを使うというやり方なんかもあります。IDCardFactoryのインスタンスをそんなにたくさん使うということもないですし、Factoryクラスにちょっとだけ変更を加えたクラスを作成したい場合には便利です。
class Factory def initialize instance_eval(yield) if block_given? end def create(owner) product = create_product owner register_product product product end def create_product(owner) end def register_product(product) end end class IDCard attr_reader :owner def initialize(owner) @owner = owner end def use end end idcard_factory = Factory.new { "@owners = Array.new" } class << idcard_factory attr_reader :owners def create_product(owner) IDCard.new(owner) end def register_product(product) @owners.push(product.owner) end protected :create_product, :register_product end products = [ idcard_factory.create("id:rubyco"), idcard_factory.create("id:rubyo"), idcard_factory.create("id:walf443") ] products.each do |product| product.use end idcard_factory.owners.each do |owner| puts owner end