3 ms·
> Visitor lets you define a new operation without changing the classes of the elements on which it operates. Yes, but that's not the real point. You can do tha
by humanrebar 10y ago
> Visitor lets you define a new operation without changing the classes of the elements on which it operates.
Yes, but that's not the real point. You can do that in other ways. Creating a new std::algorithm does that, for example.
The real purpose is letting one function be polymorphic in multiple directions. This is called multiple dispatch. Some languages support this as a built-in feature, but C++ doesn't, so a couple different design patterns were invented to fill in this gap. The Visitor pattern is the most popular and is probably the easiest to work with.
https://en.wikipedia.org/wiki/Multiple_dispatch https://en.wikipedia.org/wiki/Multiple_dispatch
That is, it lets you do this:
polymorphicOnThis->doSomething(alsoPolymorphicOnThis);
That is, for each permutation of the two types (polymorphicOnThis and alsoPolymorphicOnThis), you can define new specific behavior.
- Tyr42 10y agoAlso check out Eli Bendersky's website, he has a cool guide on multiple dispatch. http://eli.thegreenplace.net/2016/a-polyglots-guide-to-multiple-dispatch/ http://eli.thegreenplace.net/2016/a-polyglots-guide-to-multi...
- munificent 10y agoYou can use visitor to implement multiple dispatch, but that's not it's "real" point. The real point, as stated, is to allow polymorphic methods that aren't defined directly on the class. I've seen a lot of implementations of the visitor pattern, and almost all of them used it for single dispatch.
- kazinator 10y agoYou are right. The Visitor Pattern doesn't provide multiple dispatch. The Visitor Pattern relies on multiple dispatch, and so it provides a form of it to itself. The Visitor Pattern doesn't go away in a language with multiple dispatch; its boiler plate code is just reduced. What the Visitor Pattern is about is to bring together the benefits that arise from a cluster of ideas: first class functions, and generic functions. In the Common Lisp object system, OOP functions are "generic functions" which stand alone and are not tied to classes. If we have a "frobosity" method and a list of objects, we can map that list easily to get every object's frobosity: (mapcar 'frobosity objlist). That is to say, we have visited each object in objlist. Each object has effectively "accepted" a "visit" from the frobosity function. Now what we might want is instead of using generic function, to use a "funcallable object". let's use mapc instead of mapcar, since we don't need to collect the results into a list. The visitor-object itself can do something useful along those lines: (mapc visitor-object objlist) This object carries state and can do useful things, like inquire each object about its frobosity and add them together. since we don't have funcallable objects standard Lisp we can borrow the dispatch kernel of the Visitor Pattern: (mapc (lambda (list-obj) (accept visitor-object list-obj)) objlist) I.e. we use a method called accept and then curry it with a lambda to simulate visitor-object being funcallable. This lambda is just using double dispatch, just like the Visitor Pattern uses its two-dispatch simulation of double dispatch. If we have that real double dispatch, it's just more convenient to write the rest of this cruft. It's just a simple, linear bunch of definitions like: (defmethod accept ((visitor this-class) (obj that-class)) ;; handle this-class + that-class combination ) The double dispatch itself isn't the point of the pattern. The point is to use an object as a function which is mapped over some other objects, and that object does something useful: accumulates some information, pretty-prints, translates the structure to something else, etc.