Muppana Guru Prasad, Avijeet
Dash and Satyabrata Dash
Intent
Represent an operation to be performed on the elements of an object
structure. Visitor lets you define a new operation without changing the classes
of the elements on which it operates.
In 30 Seconds
The catch is "separate the structure of an object collection from the
operations performed on that collection". But again, adding new object
types to a collection often requires modifying the visitor classes that have
already been written.
Motivation
A Store Manager gives discounts on different articles depending on
price range. If the price of article were within range1, discount would be
discount1. If the price of article were within range2, discount would be
discount2 and etc. These discounts would be varying depending on seasons. The
manager wants to know what is current value of the articles in the store after
applying the discounts. If we write discount function on article, we need
to keep changing when discounts changes. Instead we can separate the
price computation functions into different class (Visitor) and allow this
visitor class to visit all different articles and apply discount on the article’s
price and sum it up. This mechanism separates the discount functionality from article.
In this case we can have Visitor class, which implement the discount
function.
Visitor Design Pattern has these structure elements:
- Visitor defines a Visitor operation for each ConcreteElement class.
- ConcreteVisitor Implements visitor operations.
- Element contains an Accept operation that would take a visitor as an
argument. - ConcreteElement implements Accept operations.
- ObjectStructure provides interface to Visitor.
The following are the consequences of the Visitor Pattern:
- It makes adding a new operation easy
- It gathers related operations.
- It make adding a new ConcreteElements hard
It may force a break in encapsulation
Sample Code
Let’s consider a simple subset of the Employee problem we discussed in the
Composite pattern. We have a simple Employee object which maintains a record of
the employee’s name, salary, vacation taken and number of sick days taken. A
simple version of this class is:
public class Employee { int sickDays, vacDays; float Salary; String Name; public Employee(String name, float salary,int vacdays, int sickdays) { vacDays = vacdays; sickDays = sickdays; Salary = salary; Name = name; } public String getName() { return Name; } public int getSickdays() { return sickDays; } public int getVacDays() { return vacDays; } public float getSalary() { return Salary; } public void accept(Visitor v) { v.visit(this); } }
Note that we have included the accept method in this class. Now let’s
suppose that we want to prepare a report of the number of vacation days that all
employees have taken so far this year. We could just write some code in the
client to sum the results of calls to each Employee’s getVacDays function,
or we could put this function into a Visitor.
Since Java is a strongly typed language, your base Visitor class needs to have a
suitable abstract visit method for each kind of class in your program. In
this first simple example, we only have Employees, so our basic abstract Visitor
class is just
public abstract class Visitor
{
public abstract void visit(Employee emp);
}
Notice that there is no indication what the Visitor does with teachclass in
either the client classes or the abstract Visitor class. We can in factwrite a
whole lot of visitors that do different things to the classes in our program.
The Visitor we are going to write first just sums the vacation data for all our
employees:
public class VacationVisitor extends Visitor { protected int total_days; public VacationVisitor() { total_days = 0; } //----------------------------- public void visit(Employee emp) { total_days += emp.getVacDays(); } //----------------------------- public int getTotalDays() { return total_days; } } Visiting the Classes Now, all we have to do to compute the total vacation taken is to go through a list of the employees and visit each of them, and then ask the Visitor for the total. VacationVisitor vac = new VacationVisitor(); for (int i = 0; i < employees.length; i++) { employees.accept(vac); } System.out.println(vac.getTotalDays());
Let’s reiterate what happens for each visit:
1. We move through a loop of all the Employees.
2. The Visitor calls each Employee’s accept method.
3. That instance of Employee calls the Visitor’s visit method.
4. The Visitor fetches the vacation days and adds them into the total.
5. The main program prints out the total when the loop is complete.
Complexities simplified
1.Double dispatch. Effectively, the Visitor pattern lets you add
operations to classes without changing them. Visitor achieves this by using a
technique called double-dispatch. "Double-dispatch"
simply means the operation that gets executed depends on the kind of request and
the types of two receivers. Accept is a
double-dispatch operation. Its meaning depends on two types: the Visitor's and
the Element's. Double-dispatching lets visitors request different operations on
each class of element.
This is the key to the Visitor pattern: The operation that gets executed
depends on both the type of Visitor and the type of Element it visits. Instead
of binding operations statically into the Element interface, you can consolidate
the operations in a Visitor and use Accept to do
the binding at run-time. Extending the Element interface amounts to defining one
new Visitor subclass rather than many new Element subclasses.
2. Who is responsible for traversing the object structure? A visitor must
visit each element of the object structure. The question is, how does it get
there? We can put responsibility for traversal in any of three places: in the
object structure, in the visitor, or in a separate iterator object.
Also Known as and Related Patterns
- Iterator
The Iterator pattern is an alternative to the Visitor pattern when the object
structure to be navigated has a linear structure. - Composite
The Visitor pattern is often used with object structures that are organized
according to the Composite pattern.
(The authors are working as Senior Developers at MindTree
Consulting Pvt. Ltd.)