Re: Improving a tutorial on the Visitor pattern
On Saturday, 8 November 2014 11:06:26 UTC+2, Paul wrote:
On Saturday, November 8, 2014 2:52:00 AM UTC, =D6=F6 Tiib wrote:
On Friday, November 7, 2014 11:14:04 PM UTC, Paul wrote:
A website promoting a book on design patterns has the code
below. Presumably the virtual destructor has been incorrectly
omitted, but that might be forgiven because it's not really
relevant to the main point.
Looking at 'main' the code has chosen to leak memory instead
of messing with destructors anyway. I would avoid reading
that site.
However, I am concerned about the code duplication here --
the line v.visit(this) is just repeated verbatim regardless
of what member of the Element hierarchy v represents.
Such double dispatch is needed for to ensure that correct
overload from visitor is selected by compiler. There are
no "multimethods" in C++ and so such duplicated one-liner
functions are necessary evil for to achieve it.
This doesn't strike me as good design.
Visitor pattern can be quite useful when the things
what visitors visit do not belong to same inheritance
hierarhy and they form far more complex object graph
in running program than raw array of 3.
Any ideas of how to resolve this? (Feel free to suggest
ideas that break the Visitor pattern).
The first alternative is to have all classes visited
in same inheritance hierarchy (in example they already are,
but in real application that may be quite expensive goal)
and then have virtual member functions like 'up' and 'down'
in all of them.
The second alternative means manually made polymorphism
from outside and that involves switch-cases on 'typeid'
(or self-made "kind") or if-else-if chains on
'dynamic_cast' and that will be very terrible code in
long run. If you see such thing somewhere then better
refactor it into virtual functions or visitor pattern.
Given example is too abstract, useless and meaningless
so it all is just pointless bloat, not worth resolving:
#include <iostream>
int main()
{
std::cout << "do Up on This\n"
"do Up on That\n"
"do Up on TheOther\n"
"do Down on This\n"
"do Down on That\n"
"do Down on TheOther\n";
}
Same effect, 10 times shorter and no memory leaks.
Thanks a lot for your reply. I've never seen a visitor example where the
things that visitors visit are not part of the same hierarchy but I see
that the basic pattern that when you accept a visitor, you ask the visito=
r
to visit, does not depend on the visited things being in the same
hierarchy.
Yes. Sometimes some artificial base class (like "Object", "Element",
"Item" or "Visitable") is used for all the visited types but there are
actually no need for that.
The code on the website is even worse than you think. The original
version (before I corrected it in the posting) has a for(i = 0; ...)=
loop instead of for(int i = 0; ...) and therefore doesn't compile.
Another visitor illustration that is prominent on google searches
is http://alumni.cs.ucr.edu/~lgao/teaching/visitor.html
This does not leak memory and does use a virtual destructor. I would
hope that this second example is ok but I haven't had a chance to run
it yet. I'd be interested to hear opinions on this second example.
If the second example handles the topic well, perhaps a good tip is that
it's a good idea to look at univ or college websites because the author
is likely to have received extensive feedback from students. I would
guess that students are far more likely to correct errors than readers
of books.
I'm about to read the Gang-of-four book on design patterns to learn
about design patterns in c++. If anyone wants to tell me: "No,
read ... instead" then I'm all ears.
All the patterns are just for solving some particular programming problems.
Imagine case that your more or less mature application contains some large
data hierarchy.
For example there are "Houses", house consists of "Rooms", those have
"Furniture" and "Equipment" in them, somewhere there is "Closet", it may
have "Drawers", some of those contain "Socks" or there is "Fridge" that
contains "Shelves" that contain "Foods" and "Beverages". All of it is more
or less dynamic run-time.
Now you need to locate and pick up all empty beer bottles in particular
house at particular moment. For that you may have to "visit" all places
where empty beer bottle can be in the house. You may write "EmptyBeerBottle=
FindingVisitor" or some more generic "SearchVisitor".
Most important is that you had no idea that you will need it when you
started to write the program. There are numerous other ways how to
implement it (and lot of those are not bad). You can extend your program wi=
th
special tracking/book-keeping for all instances of objects. You may make
all "containers" and "locations" recursively "searchable".
What you should pick? No one can tell for sure. It depends on what is
simpler and more fruitful with your almost mature code base.