2005-08-24 (Wed) [長年日記]
_ 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の方で書こうかと思ったけど、 あんまりインパクトないかなあ。