Visitor Pattern Revisited


Today, I encountered the visitor pattern in the SLD implementation inside Geotools. This is reason enough to consider this pattern in the world of Ruby. Especially because I read recently this post where visitor is supposed to be more an antipattern that a pattern. This is because Ruby supports extension of objects at runtime. Nevertheless, I need to check it out!

A simple implementation of a Visitor is accomplished in 3 steps:

1. implementation of Visitable and Visitor as modules

module Visitor
  def visit(visitable, *args)
    "Visiting #{visitable.name}"
  end
end

module Visitable
  def accept(visitor, *args)
    lvl = args[0] if !args.nil?
    lvl ||= 1
    if self.respond_to?(:children)
      lvl += 1
      self.children.each do |ch|
        visitor.visit(ch, lvl)
        ch.accept(visitor, lvl) if !ch.children.empty?
      end
    end
  end
end

2. implementation of concrete classes and mix the modules into those classes

class Node
  include Visitable

  attr_accessor :name
  attr_accessor :children

  def initialize
    self.children = []
  end

  def add_child(child)
    self.children = [] if self.children.nil?
    self.children << child
  end
end

class ChangeNameVisitor
  include Visitor

  def visit(visitable, *args)
    visitable.name = visitable.name.reverse
    lvl = args[0] if !args.nil?
    str = ""
    lvl.times {
      str << '.'
    }
    puts str + super
  end
end

3. Usage

root = Node.new
5.times do |n|
  node = Node.new
  node.name = "Node_#{n}"
  root.add_child(node)
  5.times do |m|
    sub_node = Node.new
    sub_node.name = "node_#{n}_#{m}"
    node.add_child(sub_node)
    5.times do |l|
      sub_sub_node = Node.new
      sub_sub_node.name = "node_#{n}_#{m}_#{l}"
      sub_node.add_child(sub_sub_node)
    end
  end
end

v = ChangeNameVisitor.new
root.accept(v)

Output

..Visiting 0_edoN
...Visiting 0_0_edon
....Visiting 0_0_0_edon
....Visiting 1_0_0_edon
....Visiting 2_0_0_edon
....Visiting 3_0_0_edon
....Visiting 4_0_0_edon
...Visiting 1_0_edon
....Visiting 0_1_0_edon
....Visiting 1_1_0_edon
....Visiting 2_1_0_edon
....Visiting 3_1_0_edon
....Visiting 4_1_0_edon
................................

Actually, what I just did is traversing all nodes of root and passing them to the particular visitor. But in Ruby, this separation of concerns doesn’t play a role (as assumed in this post). The “Open Class Idiom” is more natural. It saves much code and makes it more readable:

#... more code

#re-open it!
class Node

  def change_name
    self.name = self.name.reverse
  end

end

———————

Edit July, 24. 2013:

Just stumbled upon the implementation of Cucumber. Cucumber is using the Visitor pattern as well for the implementation of the TreeWalker ;-):

      def visit_steps(steps)
        broadcast(steps) do
          steps.accept(self)
        end
      end
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s