From: Robert Klemme Date: 2004-05-28T21:59:58+09:00 Subject: Re: Visitor Pattern in Ruby "Charles Comstock" schrieb im Newsbeitrag news:Pine.LNX.4.44.0405261453420.16028-100000@earwig.int.cec.wustl.edu... > I recently wrote a small compiler in java in which we used the visitor > design pattern to walk an abstract syntax tree hiearchy. I was most > impressed with the ease with which a reflective visitor allowed me to > walk the tree. So recently having some free time I set about to > implement a similar visitor pattern for Ruby, which could easily be > dropped in as a module. I believe the code below does this in a > reasonably elegant fasion. I have examples of it's use posted at the > bottom of http://www.rubygarden.org/ruby?VisitorPattern I thought it > an interesting solution for implementing double-dispatch, while still > providing alot of power and flexability. I was curious considering > the existing design patterns in the ruby library (Observer, Singleton, > etc.) if there was any interest in including this design pattern as well? > > Whether or not there is interest, I was curious what, if any thread > safety or exception handling anyone would suggest in this code, or if > it should simply be handled by visitor code that uses this library. Yes, I'd leave thread safety completely out of this. You can't do it properly here and most code that uses it won't need it. The only problem I can see at the moment is with classes that are not in the global scope: >> module Foo; class Bar; end; end => nil >> Foo::Bar => Foo::Bar >> Foo::Bar.to_s => "Foo::Bar" This string is likely to make problems in a method name: >> class X >> def visit_Foo::Bar >> end >> end NameError: undefined local variable or method `visit_Foo' for X:Class from (irb):5 Maybe you just map "::" to "_" or something similar. An alternative approach could be to use the visitor to get a proc for the current instance, like in class Visitor def visit(obj) bl = nil obj.class.ancestors.each do |cl| bl ||= visitors[cl] # hash lookup end bl.call( obj ) if bl end end Kind regards robert > Thanks, > Charles Comstock > > #file visitor.rb > module Visitable > def accept(visitor,&block) > for klass in self.class.ancestors do > break if (v = visitor.methods.include?("visit_#{klass}")) > end > if v > visitor.__send__("visit_#{klass}",self,&block) > else > visitor.default_visit(self,&block) > end > end > def Visitable.included(kls) > kls.module_eval <<-"end_eval" > def self.inherited(child) > child.module_eval "include Visitable" > end > end_eval > end > end > >