Visitor

Objective: Separate Auxiliary procedures and algorithms from the object they operate.

We can consider the Visitor pattern a more complex version of double dispatch. It uses the double dispatch principle to make it work.

On top of double dispatch, Visitor lets you add “external” operations to a whole class hierarchy without changing the existing code of these classes.

Visitor isn’t a very common pattern because of its complexity and narrow applicability.

Related to Double Dispatch. Refactoring Guru

Applications

  • Use the Visitor when you need to perform an operation on all elements of a complex object structure (trasversing a tree).
  • Use the Visitor to clean up the business logic of auxiliary behaviors.

Example

class Node
  def accept visitor
    raise NotImpelementedError.new
  end
end

module Visitable
  def accept visitor
    visitor.visit(self)
  end
end

class IntegerNode < Node
  include Visitable

  attr_reader :value
  def initialize value
    @value = value
  end
end

class StringNode < Node
  include Visitable

  attr_reader :value
  def initialize value
    @value = value
  end
end

class Ast < Node
  def initialize
    @nodes = []
    @nodes << IntegerNode.new(2)
    @nodes << StringNode.new("3")
  end

  def accept visitor
    @nodes.each do |node|
      node.accept visitor
    end
  end
end

class BaseVisitor
  def visit subject
    method_name = "visit_#{subject.class}".intern
    send(method_name, subject )
  end
end

class DoublerVisitor < BaseVisitor
  def visit_IntegerNode subject
    puts subject.value * 2
  end

  def visit_StringNode subject
    puts subject.value.to_i * 2
  end
end

class TriplerVisitor < BaseVisitor
  def visit_IntegerNode subject
    puts subject.value * 3
  end

  def visit_StringNode subject
    puts subject.value.to_i * 3
  end
end

ast = Ast.new
puts "Doubler:"
ast.accept DoublerVisitor.new
puts "Tripler:"
ast.accept TriplerVisitor.new

# =>
Doubler:
4
6
Tripler:
6
9

Real World Usage

Arel uses this pattern to build specific database queries. See Arel PostgreSQL