120 likes | 211 Views
Behavioral Pattern: Visitor. Chapter 5 – Page 224. 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.
E N D
Behavioral Pattern: Visitor Chapter 5 – Page 224 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.)
The Visitor Pattern Chapter 5 – Page 225 • 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 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. • 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.
Non-Software Example: Cab-Calling Chapter 5 – Page 226 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.
Software Example: Car Parts Chapter 5 – Page 227 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.
Car Parts Visitor Pattern C++ Code Chapter 5 – Page 228 #include <string> #include <iostream> #include <vector> using namespace std; class Wheel; class Engine; class Body; class Car; // Interface to all car parts structCarElementVisitor { virtual void visit(Wheel& wheel) const = 0; virtual void visit(Engine& engine) const = 0; virtual void visit(Body& body) const = 0; virtual void visitCar(Car& car) const = 0; virtual ~CarElementVisitor() {} }; // Interface to one car part structCarElement { virtual void accept(constCarElementVisitor& visitor) = 0; virtual ~CarElement() {} };
// Wheel element, there are four wheels with unique names class Wheel : publicCarElement { public: explicit Wheel(const string& name) : name(name) {} const string& getName() const { return name; } void accept(constCarElementVisitor& visitor) { visitor.visit(*this); } private: string name; }; // Engine element class Engine : publicCarElement { public: void accept(const CarElementVisitor& visitor) { visitor.visit(*this); } }; // Body element class Body : publicCarElement { public: void accept(constCarElementVisitor& visitor) { visitor.visit(*this); } }; Chapter 5 – Page 229
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. classCarElementDiagnosticVisitor : publicCarElementVisitor { 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; } voidvisitCar(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. classCarElementTestDriveVisitor : publicCarElementVisitor { 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; } voidvisitCar(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; CarElementDiagnosticVisitorprintVisitor; CarElementTestDriveVisitordoVisitor; printVisitor.visitCar(car); doVisitor.visitCar(car); }
Visitor Pattern Advantages Chapter 5 – Page 234 • 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.