Chapter 5

When an algorithm is separated from an object structure upon
which it operates, new operations can be added to existing
object structures without modifying those structures
The Visitor Pattern allows new virtual functions to be added to
a family of classes without modifying the classes themselves;
instead, one creates a visitor class that implements all of the
appropriate specializations of the virtual function.
This is particularly useful when there are a
large number of instances of a small number
of classes and some operation must be
performed that involves all or most of them.
(While powerful, the Visitor Pattern is more limited than
conventional virtual functions since it isn’t possible to create
visitors for objects without adding a small callback method
inside each class and the callback method in each of the
classes is not inheritable.)
Chapter 5 – Page 224
Behavioral Pattern: Visitor
Chapter 5 – Page 225
The Visitor Pattern
The Visitor declares a visit operation for
each class of ConcreteElement in the
object structure. The operation's name and
signature identifies the class that sends the
Visit request to the visitor. That lets the
visitor determine the concrete class of the
element being visited. Then the visitor can
access the elements directly through its
particular interface.
The ConcreteVisitor implements each operation
declared by the Visitor. Each operation implements a
fragment of the algorithm defined for the
corresponding class or object in the structure. The
ConcreteVisitor provides the context for the algorithm
and stores its local state. This state often accumulates
results during the traversal of the structure.
The Element defines an Accept operation that takes a
visitor as an argument, while the ConcreteElement
implements an Accept operation that takes a visitor as
an argument.
The Object Structure can
enumerate its elements and
may provide a high-level
interface to allow the visitor
to visit its elements. It may
either be a Composite
(pattern) or a collection
such as a list or a set.
When a person calls a taxi company
he or she becomes part of the
company's list of customers (the
ObjectStructure).
The taxi company’s dispatcher (the
Client) then sends a cab (the Visitor)
to the customer (the Element).
Upon entering the taxi, the customer is no longer in
control of his or her own transportation, the taxi
(i.e., the taxi driver) is.
Chapter 5 – Page 226
Non-Software Example: Cab-Calling
The Car (ObjectStructure)
is composed of various
CarElements (Wheels,
Engine, Body).
The CarElementVisitor
provides an interface for
“visiting” each type of
CarElement.
The DiagnosticVisitor and the
TestDriveVisitor implement
specific algorithms for dealing
with the Car’s CarElements
without altering the definitions
of the Car or the CarElements.
Chapter 5 – Page 227
Software Example: Car Parts
#include <string>
#include <iostream>
#include <vector>
using namespace std;
class
class
class
class
Wheel;
Engine;
Body;
Car;
// Interface to all car parts
struct CarElementVisitor
{
virtual void visit(Wheel& wheel)
virtual void visit(Engine& engine)
virtual void visit(Body& body)
virtual void visitCar(Car& car)
virtual ~CarElementVisitor() {}
};
const
const
const
const
=
=
=
=
0;
0;
0;
0;
// Interface to one car part
struct CarElement
{
virtual void accept(const CarElementVisitor& visitor) = 0;
virtual ~CarElement() {}
};
Chapter 5 – Page 228
Car Parts Visitor Pattern C++ Code
// Engine element
class Engine : public CarElement
{
public:
void accept(const CarElementVisitor& visitor)
{
visitor.visit(*this);
}
};
// Body element
class Body : public CarElement
{
public:
void accept(const CarElementVisitor& visitor)
{
visitor.visit(*this);
}
};
Chapter 5 – Page 229
// Wheel element, there are four wheels with unique names
class Wheel : public CarElement
{
public:
explicit Wheel(const string& name) : name(name) {}
const string& getName() const
{
return name;
}
void accept(const CarElementVisitor& visitor)
{
visitor.visit(*this);
}
private:
string name;
};
Chapter 5 – Page 230
// Car, all car elements (parts) together in one vector
class Car
{
public:
vector<CarElement*>& getElements()
{
return elements;
}
Car()
{
// assume that neither push_back nor Wheel(const string&) may throw
elements.push_back( new Wheel("front left") );
elements.push_back( new Wheel("front right") );
elements.push_back( new Wheel("back left") );
elements.push_back( new Wheel("back right") );
elements.push_back( new Body() );
elements.push_back( new Engine() );
}
~Car()
{
vector<CarElement*>::iterator it;
for (it = elements.begin(); it != elements.end(); ++it)
{
delete *it;
}
}
private:
vector<CarElement*> elements;
};
Chapter 5 – Page 231
// DiagnosticVisitor adds a diagnostic capability to the
// Car class without modifying the Car class itself.
class CarElementDiagnosticVisitor : public CarElementVisitor
{
public:
void visit(Wheel& wheel) const
{
cout << "Testing the car's " << wheel.getName()
<< " wheel for tread and punctures" << endl;
}
void visit(Engine& engine) const
{
cout << "Examining the car's engine for leaks and loose belts" << endl;
}
void visit(Body& body) const
{
cout << "Examining the car's body for dents and scratches" << endl;
}
void visitCar(Car& car) const
{
cout << "Performing diagnostic testing on the car" << endl;
vector<CarElement*>& elems = car.getElements();
// Cycle through the car's elements, issuing a
// callback from each car element to this visitor.
vector<CarElement*>::iterator it;
for (it = elems.begin(); it != elems.end(); ++it )
{
(*it)->accept(*this);
}
cout << "Diagnostic testing complete" << endl << endl;
}
};
Chapter 5 – Page 232
// TestDriveVisitor adds a test drive capability to the
// Car class without modifying the Car class itself.
class CarElementTestDriveVisitor : public CarElementVisitor
{
public:
void visit(Wheel& wheel) const
{
cout << "Kicking the car's " << wheel.getName() << " tire" << endl;
}
void visit(Engine& engine) const
{
cout << "Starting the car's engine" << endl;
}
void visit(Body& body) const
{
cout << "Driving the car for a road test" << endl;
}
void visitCar(Car& car) const
{
cout << "Test driving the car" << endl;
vector<CarElement*>& elems = car.getElements();
// Cycle through the car's elements, issuing a
// callback from each car element to this visitor.
vector<CarElement*>::iterator it;
for (it = elems.begin(); it != elems.end(); ++it )
{
(*it)->accept(*this);
}
cout << "Test drive complete" << endl << endl;
}
};
Chapter 5 – Page 233
void main()
{
Car car;
CarElementDiagnosticVisitor printVisitor;
CarElementTestDriveVisitor doVisitor;
printVisitor.visitCar(car);
doVisitor.visitCar(car);
}
• The Visitor Pattern permits the addition of functions to class
libraries for which either the source code or the ability to
change the source code is unavailable.
• This pattern is also helpful when it is necessary to obtain data
from a disparate collection of unrelated classes and use it to
present the results of a global calculation to the user
program.
• It can be used to gather related operations into a single class
rather than forcing the designer to change or derive classes
to add these operations.
Chapter 5 – Page 234
Visitor Pattern Advantages