トップ «前の日記(2005-08-23 (Tue)) 最新 次の日記(2005-08-26 (Fri))» 編集   RSS 1.0 FEED  

Journal InTime


2005-08-24 (Wed) [長年日記]

_ [Ruby] DoubleDispatchable

Visitorパターンみたいなダブルディスパッチは好きなのだが、 acceptメソッドみたいなのを何回も書いてると面倒だと思う時がある。 そこでこんなモジュールを書いてみた。

module DoubleDispatchable
  def self.append_features(klass)
    super(klass)
    klass.class_eval do
      @@double_dispatch_methods = {}
    end
    def klass.double_dispatch(method, prefix)
      @@double_dispatch_methods[method.to_s] = prefix.to_s
      define_method(method) do
        raise SubclassResponsibilityError.new
      end
    end
    def klass.inherited(subclass)
      for method, prefix in @@double_dispatch_methods
        method2 = prefix +
          subclass.name.slice(/[A-Za-z]+\z/).gsub(/[A-Z]/) { |s|
          "_" + s.downcase
        }
        subclass.class_eval(<<-EOF)
          def #{method}(obj, *args)
            obj.#{method2}(self, *args)
          end
        EOF
      end
    end
  end
end

これを、

class Query
  include DoubleDispatchable

  double_dispatch :accept, :visit
end

のように使うと、

class AndQuery < Query
end

のようにサブクラス化した時点で勝手に以下のようなメソッドを 作ってくれる。

def accpet(obj, *args)
  obj.visit_and_query(self, *args)
end

もともとはVisitorパターン専用にAcceptableというモジュールを 書いていたのだが、汎用化して上記のような形にしてみた。

調べてみると、Ruby: VisitorPattern でほとんど同じ手法が紹介されてるではないか。 Blogの方で書こうかと思ったけど、 あんまりインパクトないかなあ。