Секреты LINQ. Автогенерируемые свойства. Инициализаторы объектов и коллекций, страница 26

In object-oriented programming and software engineering, the visitor design pattern is a way of separating an algorithm from an object structure upon which it operates. A practical result of this separation is the ability to add new operations to existing object structures without modifying those structures. Thus, using the visitor pattern helps conformance with the open/closed principle.

In essence, the visitor allows one to add new virtual functions 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. The visitor takes the instance reference as input, and implements the goal through double dispatch. While powerful, the visitor pattern is more limited than conventional virtual functions. It is not possible to create visitors for objects without adding a small callback method inside each class. In naive implementations, the callback method in each of the classes is not inheritable.

Details

The idea is to use a structure of element classes, each of which has an accept() method that takes a visitor object as an argument. Visitor is an interface that has a visit() method for each element class. The accept() method of an element class calls back the visit() method for its class. Separate concrete visitor classes can then be written that perform some particular operations, by implementing these operations in their respective visit() methods.

One of these visit() methods of a concrete visitor can be thought of as a method not of a single class, but rather a method of a pair of classes: the concrete visitor and the particular element class. Thus the visitor pattern simulates double dispatch in a conventional single-dispatch object-oriented language such as Java, Smalltalk, and C++. For an explanation of how double dispatch differs from function overloading, see Double dispatch is more than function overloading in the double dispatch article. In the Java language, two techniques have been documented which use reflection to simplify the mechanics of double dispatch simulation in the visitor pattern: getting rid of accept() methods (the Walkabout variation), and getting rid of extra visit() methods.

The visitor pattern also specifies how iteration occurs over the object structure. In the simplest version, where each algorithm needs to iterate in the same way, the accept() method of a container element, in addition to calling back the visit() method of the visitor, also passes the visitor object to the accept() method of all its constituent child elements.....

Because the Visitor object has one principal function (manifested in a plurality of specialized methods) and that function is called visit(), the Visitor can be readily identified as a potential function object. Likewise, the accept() function can be identified as a function applicator, a mapper, which knows how to traverse a particular type of object and apply a function to its elements. Lisp's object system with its multiple dispatch does not replace the Visitor pattern, but merely provides a more concise implementation of it in which the pattern all but disappears.

class SpaceShip {};

class GiantSpaceShip : public SpaceShip {};

class Asteroid

{

public:

virtual void CollideWith(SpaceShip&) {

cout << "Asteroid hit a SpaceShip" << endl;

}

virtual void CollideWith(GiantSpaceShip&) {

cout << "Asteroid hit a GiantSpaceShip" << endl;

}

};

class ExplodingAsteroid : public Asteroid {

public:

virtual void CollideWith(SpaceShip&) {

cout << "ExplodingAsteroid hit a SpaceShip" << endl;

}

virtual void CollideWith(GiantSpaceShip&) {

cout << "ExplodingAsteroid hit a GiantSpaceShip" << endl;

}

};

Suppose SpaceShip and GiantSpaceShip both have the function

virtual void CollideWith(Asteroid& asteroid) { asteroid.CollideWith(*this); }