views:

186

answers:

1

Hey there, I have read the few posts here on when/how to use the visitor pattern, and some articles/chapters on it, and it makes sense if you are traversing an AST and it is highly structured, and you want to encapsulate the logic into a separate "visitor" object, etc. But with Ruby, it seems like overkill because you could just use blocks to do nearly the same thing.

I would like to pretty_print xml using Nokogiri. The author recommended that I use the visitor pattern, which would require I create a FormatVisitor or something similar, so I could just say "node.accept(FormatVisitor.new)".

The issue is, what if I want to start customizing all the stuff in the FormatVisitor (say it allows you to specify how nodes are tabbed, how attributes are sorted, how attributes are spaced, etc.).

  • One time I want the nodes to have 1 tab for each nest level, and the attributes to be in any order
  • The next time, I want the nodes to have 2 spaces, and the attributes in alphabetical order
  • The next time, I want them with 3 spaces and with two attributes per line.

I have a few options:

  • Create an options hash in the constructor (FormatVisitor.new({:tabs => 2})
  • Set values after I have constructed the Visitor
  • Subclass the FormatVisitor for each new implementation
  • Or just use blocks, not the visitor

Instead of having to construct a FormatVisitor, set values, and pass it to the node.accept method, why not just do this:


node.pretty_print do |format|
  format.tabs = 2
  format.sort_attributes_by {...}
end

That's in contrast to what I feel like the visitor pattern would look like:


visitor = Class.new(FormatVisitor) do
  attr_accessor :format
  def pretty_print(node)
    # do something with the text
    @format.tabs = 2 # two tabs per nest level
    @format.sort_attributes_by {...}
  end
end.new
doc.children.each do |child|
  child.accept(visitor)
end

Maybe I've got the visitor pattern all wrong, but from what I've read about it in ruby, it seems like overkill. What do you think? Either way is fine with me, just wondering what how you guys feel about it.

Thanks a lot, Lance

+5  A: 

I would go with what is simple and works. I don't know the details, but what you wrote compared with the Visitor pattern, looks simpler. If it also works for you, I would use that. Personally, I am tired with all these techniques that ask you to create a huge "network" of interelated classes, just to solve one small problem.

Some would say, yeah, but if you do it using patterns then you can cover many future needs and blah blah. I say, do now what works and if the need arises, you can refactor in the future. In my projects, that need almost never arises, but that's a different story.

Petros
agreed. If you need stronger maintainability, make a method that can generate your blocks, but I feel that the visitor pattern can be reconstructed using native Ruby code, just like the Factory pattern is pretty much constructible using native initializers.
James Deville
Actually the real benefit of patterns is that they aid maintenance by making it easy for others to understand what you are trying to achieve with the pattern. Too often the intention is lost in the implementation - if you recognise the pattern, you have q better chance of recognising the intent.
Chris McCauley
Thanks a lot, these are all great points.
viatropos