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の方で書こうかと思ったけど、 あんまりインパクトないかなあ。
[ツッコミを入れる]