 |
BorlandTalk.com Borland discussion newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
Peter Morris [Droopy eyes Guest
|
Posted: Fri Feb 04, 2005 10:09 pm Post subject: Visitor pattern - *mehhh* |
|
|
"mehhh" is a disgruntled, unimpressed sound.
I've just re-read the visitor pattern in the GOF design patterns book.
I had to re-read it because I have always thought it was a silly idea, and
seeing as many other people like it so much I must misunderstand it or
something.
So, I'm reading the chapter and my brain keeps telling me
"If you add a new node class, you have to add a virtual method to the base
node visitor, and if you use an abstract method as it suggests then also to
every subclass too"
So, again I think I am wrong, it can't be that ridiculous, so I read on.
Finally I stumble across this statement "Adding new ConcreteElement classes
is hard...........Each new ConcreteElement gives rise to a new abstract
operation on Visitor and a corresponding implementation in every
ConcreteVisitor class", then suddenly the penny drops, it *IS* ridiculous.
At least the way the GOF book implements it is ridiculous. To me it seems
that what they are trying to achieve is a callback of some kind.
RootElement.AcceptVisitor(Self);
Where RootElement would be
procedure TRootElement.AcceptVisitor(Visitor: TVisitor);
begin
Visitor.Execute(Self);
end;
The visitor in question would simply override Execute, and that would
perform the visitor's default action on the node. If it were a
"PrintVisitor" it would print the node, etc.
Why does the GOF book insist that you have an abstract method for each type
of visitor in the base visitor class?
Finally, the part I still just don't understand about the visitor pattern is
this. If the visitor pattern simply executes a method for each node in a
list why do we not just have a FOR loop or, if necessary, an iterator and
just execute a method against each node at a time? Am I missing some kind
of simplicity? Is it something like recursion, where it looks dead simple
but is secretly doing something very impressive?
/me prepares to rip the "Visitor" pattern out of the book :-)
--
Pete
====
ECO Modeler, Audio compression components, DIB graphics controls,
FastStrings
http://www.droopyeyes.com
Read or write articles on just about anything
http://www.HowToDoThings.com
My blog
http://blogs.slcdug.org/petermorris/
|
|
| Back to top |
|
 |
Jim Cooper Guest
|
Posted: Fri Feb 04, 2005 10:56 pm Post subject: Re: Visitor pattern - *mehhh* |
|
|
| Quote: | So, I'm reading the chapter and my brain keeps telling me
"If you add a new node class, you have to add a virtual method to the base
node visitor, and if you use an abstract method as it suggests then also to
every subclass too"
|
Yes, if the base visitor class has abstract methods, you have to add the
new method to every descendant (same deal if you use an interface
instead of an abstract base class). Typically you do not use abstract
methods though. In most situations to want a given visitor to ignore
some nodes, hence a default behaviour of "do nothing" is appropriate
almost always. I've never come across a use where there wasn't some
required default (eg the printing in your post's example)
| Quote: | At least the way the GOF book implements it is ridiculous.
|
If you read on, you will find advice of when *not* to use visitor - if
you anticipate constantly adding to the element hierarchy, it is a bad
idea to use Visitor (pp 333,336). An example of this is the tiOPF
framework (which also requires way too many visitor class, BTW)
| Quote: | To me it seems that what they are trying to achieve is a callback of some kind.
|
Yes. The callback depends on both the class of the node doing the
calling, and the class of the Visitor being called (hence the term
"double dispatch").
| Quote: | Why does the GOF book insist that you have an abstract method for each type
of visitor in the base visitor class?
|
It doesn't. Certainly the example doesn't have abstract methods anywhere.
| Quote: | why do we not just have a FOR loop or, if necessary, an iterator and
just execute a method against each node at a time? Am I missing some kind
of simplicity?
|
I think so. The big advantage of Visitor is when navigation through your
element structure is complex. It puts the navigation in one place - in
the element structure. Then you can define many visitors which do
different things requiring that navigation. Changes to the element
hierarchy mean changes to the navigation happen in one place only.
If navigation is simple (eg a list) then yes, Iterator is a simpler
option. Also, as you say, if the element hierarchy is often added to and
you have many Visitors, maintaining them is a pain. IMO it is also
pointless having only one Visitor - you may as well put the operation in
the element hierarchy (see the example here
http://www.tabdee.ltd.uk/Software/Papers/MoreDesignPatterns/MoreDesignPatterns.html
for code which does that both ways)
In principle, you can have both Visitors and Iterators on the same
element structure. Iterators have the navigation within them, Visitors
have the navigation with the element structure. (There are other
differences, too, of course, like iterators often allow moving back and
forwards, whereas visitors typically just execute)
| Quote: | Is it something like recursion, where it looks dead simple
but is secretly doing something very impressive?
|
Well, I think it is impressive and useful However, you can overuse
it (or use it badly). There are sometimes simpler solutions. But it is
elegant. However, if you think it looks dead simple then try standing up
in front of a room full of people and explaining it :-)
| Quote: | /me prepares to rip the "Visitor" pattern out of the book
|
I hope I've convinced you not to do that :-)
Cheers,
Jim Cooper
__________________________________________
Jim Cooper [email]jcooper (AT) tabdee (DOT) ltd.uk[/email]
Tabdee Ltd http://www.tabdee.ltd.uk
TurboSync - Connecting Delphi to your Palm
__________________________________________
|
|
| Back to top |
|
 |
Joanna Carter (TeamB) Guest
|
Posted: Fri Feb 04, 2005 11:12 pm Post subject: Re: Visitor pattern - *mehhh* |
|
|
"Jim Cooper" <jcooper (AT) tabdee (DOT) ltd.uk> a écrit dans le message de news:
4203fd8a$1 (AT) newsgroups (DOT) borland.com...
| Quote: | I hope I've convinced you not to do that
|
If I can just add a simple rule of thumb for using the Visitor pattern :
If you have an established hierarchy that may need additional behaviours
without altering the hierarchy, then it is a good thing.
If you have an hierarchy that is likely to change, then don't touch it with
the proverbial ten-foot bargepole )
Joanna
--
Joanna Carter (TeamB)
Consultant Software Engineer
TeamBUG support for UK-BUG
TeamMM support for ModelMaker
|
|
| Back to top |
|
 |
Gerrit Beuze Guest
|
Posted: Sat Feb 05, 2005 7:48 am Post subject: Re: Visitor pattern - *mehhh* |
|
|
A visitor is the implementatin of a matric VMT instead of vector (linear) VMT.
Both the type of the visitor AND the type of the visited class determine which
method gets called. First there's the VMT for the visited class (AcceptVisitor)
which in tunr calls a virtual method on the visitor class (VisitField, VisitMethod)
For example:
If you have lists with a single class type in it, iterators are just as good.
The big difference with a iterator comes when a list contains different class types
For example: take ModelMaker (Code Explorer/UML Explorer whatever)
this has a member list that contains class members.
Members can only be Field | Method | Property | Event | Method Resolution Clause
This means the hierchy is fixed.
If I use an iterator to fill a listview I'd have code like this:
begin
for I := 0 to List.Count do // or create an enumerator as you wish
case Members[I].MemberType of
mtField: do for field
mtMethod: do for field
mtPropety: etc etc
end;
end;
I use a visitor I can simply use this:
begin
Visitor := TMemberListVisitor.Create;
for I := 0 to Members.Count - 1 do Visitor.Accept(Members[I]);
end;
If I wanted to add code to display members in a class symbol all
I had to do was create a new visitor
Again, this could be done using a case statement (and that's all a VMT is really)
based on type (or worse:
if Member is TField then do field
else
if Member is TMethod then do method
if Member is TPropety then etc etc etc
The ModelMaker Design Patterns manual comes with detailed examples
Available for download at the ModelMaker download page.
http://www.modelmakertools.com/modelmaker/download.html
Gerrit Beuze
ModelMaker Tools
"Peter Morris [Droopy eyes software]" <pete (AT) remove (DOT) this.droopyeyes.com> wrote
| Quote: | "mehhh" is a disgruntled, unimpressed sound.
I've just re-read the visitor pattern in the GOF design patterns book.
I had to re-read it because I have always thought it was a silly idea, and
seeing as many other people like it so much I must misunderstand it or
something.
So, I'm reading the chapter and my brain keeps telling me
"If you add a new node class, you have to add a virtual method to the base
node visitor, and if you use an abstract method as it suggests then also to
every subclass too"
So, again I think I am wrong, it can't be that ridiculous, so I read on.
Finally I stumble across this statement "Adding new ConcreteElement classes
is hard...........Each new ConcreteElement gives rise to a new abstract
operation on Visitor and a corresponding implementation in every
ConcreteVisitor class", then suddenly the penny drops, it *IS* ridiculous.
At least the way the GOF book implements it is ridiculous. To me it seems
that what they are trying to achieve is a callback of some kind.
RootElement.AcceptVisitor(Self);
Where RootElement would be
procedure TRootElement.AcceptVisitor(Visitor: TVisitor);
begin
Visitor.Execute(Self);
end;
The visitor in question would simply override Execute, and that would
perform the visitor's default action on the node. If it were a
"PrintVisitor" it would print the node, etc.
Why does the GOF book insist that you have an abstract method for each type
of visitor in the base visitor class?
Finally, the part I still just don't understand about the visitor pattern is
this. If the visitor pattern simply executes a method for each node in a
list why do we not just have a FOR loop or, if necessary, an iterator and
just execute a method against each node at a time? Am I missing some kind
of simplicity? Is it something like recursion, where it looks dead simple
but is secretly doing something very impressive?
/me prepares to rip the "Visitor" pattern out of the book :-)
--
Pete
====
ECO Modeler, Audio compression components, DIB graphics controls,
FastStrings
http://www.droopyeyes.com
Read or write articles on just about anything
http://www.HowToDoThings.com
My blog
http://blogs.slcdug.org/petermorris/
|
|
|
| Back to top |
|
 |
Peter Morris [Droopy eyes Guest
|
Posted: Sat Feb 05, 2005 8:37 am Post subject: Re: Visitor pattern - *mehhh* |
|
|
Hi Gerrit
| Quote: | begin
for I := 0 to List.Count do // or create an enumerator as you wish
case Members[I].MemberType of
mtField: do for field
mtMethod: do for field
mtPropety: etc etc
end;
end;
|
I thought it would make sense just to call SomeClass.Execute(member), and if
the visitor in question doesn't do anything with a Method then it ignores it
if Member.MemberType <> mtMethod then
Exit;
What I don't understand is why the concrete elements need to know which
visitor method to execute. Why can't they just call "Execute" and let the
visitor decide what it wants to do?
--
Pete
====
ECO Modeler, Audio compression components, DIB graphics controls,
FastStrings
http://www.droopyeyes.com
Read or write articles on just about anything
http://www.HowToDoThings.com
My blog
http://blogs.slcdug.org/petermorris/
|
|
| Back to top |
|
 |
Peter Morris [Droopy eyes Guest
|
Posted: Sat Feb 05, 2005 8:39 am Post subject: Re: Visitor pattern - *mehhh* |
|
|
| Quote: | I think so. The big advantage of Visitor is when navigation through your
element structure is complex. It puts the navigation in one place - in the
element structure. Then you can define many visitors which do different
things requiring that navigation. Changes to the element hierarchy mean
changes to the navigation happen in one place only.
|
If I recall correctly though, the Iterator can do this too. I seem to
remember an example of an iterator internally creating child Iterators
(recursively) so that a tree-like structure can be navigated in a linear
fasion.
--
Pete
====
ECO Modeler, Audio compression components, DIB graphics controls,
FastStrings
http://www.droopyeyes.com
Read or write articles on just about anything
http://www.HowToDoThings.com
My blog
http://blogs.slcdug.org/petermorris/
|
|
| Back to top |
|
 |
Jim Cooper Guest
|
Posted: Sat Feb 05, 2005 10:51 am Post subject: Re: Visitor pattern - *mehhh* |
|
|
| Quote: | What I don't understand is why the concrete elements need to know which
visitor method to execute.
|
Well, each concrete element class knows what class it is, so it's not a
big deal. However, I let the compiler work it out because I have
overloaded Visit methods :
TVisitor = class
public
procedure Visit(Element : TElementType1); overload;
procedure Visit(Element : TElementType2); overload;
procedure Visit(Element : TElementType3); overload;
procedure Visit(Element : TElementType4); overload;
end;
| Quote: | Why can't they just call "Execute" and let the
visitor decide what it wants to do?
|
They can do (it's not generally called Execute though). Some people
prefer not to use overloaded methods and prefer VisitElementType1,
VisitElementType2 etc for clarity (for them, at least)
However the point of Visitor is that the class of both the visitor and
the thing being visited combine to determine which behaviour happens.
Cheers,
Jim Cooper
__________________________________________
Jim Cooper [email]jcooper (AT) tabdee (DOT) ltd.uk[/email]
Tabdee Ltd http://www.tabdee.ltd.uk
TurboSync - Connecting Delphi to your Palm
__________________________________________
|
|
| Back to top |
|
 |
Jim Cooper Guest
|
Posted: Sat Feb 05, 2005 11:03 am Post subject: Re: Visitor pattern - *mehhh* |
|
|
| Quote: | If I recall correctly though, the Iterator can do this too.
|
Yes, it can. But the difference is in where the navigation code is, and
in how you choose what to do to each element. Sometimes Iterator may be
the better choice. However, it is not always the better choice. Try
rewriting the pretty printing example of mine with Iterator and see
which you think is the more elegant.
| Quote: | I seem to remember an example of an iterator internally creating child Iterators
(recursively) so that a tree-like structure can be navigated in a linear
fasion.
|
And there's a good example. There the navigation code is in the
iterator, so changes to the element hierarchy require changes to the
iterator (possibly many iterators). The navigation code is typically
more complex (your example certainly sounds it <g>). In the Visitor
pattern the code for the navigation is in the element hierarchy itself.
Typically changes are lower impact that way. Also, you would need some
sort of factory in the iterator to determine what to do with each type
of element node it came across. In Visitor that's hard-coded in (and
there isn't a problem with it being that way).
Visitor is typically used to perform one operation, eg pretty printing
XML code, where you traverse the element structure in one go. Iterator
is intended more as a navigation method, so tends to be used in
different situations, where you want to traverse the element structure
under some sort of external control.
However, you of course always have a choice. Sometimes it might be the
right thing to do to treat a complex object structure as a list - so use
Iterator. If you need to traverse the structure to so something, but
only one operation needs to do that, maybe you should put the code
directly into the elements (perhaps delegating work to Strategy objects,
etc). If you need to perform several different operations on a complex
structure, use Visitor. If the structure is not complex, but is just a
list, maybe Iterator is appropriate.
To dismiss Visitor out of hand is to rob yourself of a useful, elegant
technique, though.
Cheers,
Jim Cooper
__________________________________________
Jim Cooper [email]jcooper (AT) tabdee (DOT) ltd.uk[/email]
Tabdee Ltd http://www.tabdee.ltd.uk
TurboSync - Connecting Delphi to your Palm
__________________________________________
|
|
| Back to top |
|
 |
Peter Morris [Droopy eyes Guest
|
Posted: Sat Feb 05, 2005 11:24 am Post subject: Re: Visitor pattern - *mehhh* |
|
|
| Quote: | However the point of Visitor is that the class of both the visitor and the
thing being visited combine to determine which behaviour happens.
|
Could you give me an example of how this is useful? Both determining I
mean. I see a single visitor as performing a single action.
--
Pete
====
ECO Modeler, Audio compression components, DIB graphics controls,
FastStrings
http://www.droopyeyes.com
Read or write articles on just about anything
http://www.HowToDoThings.com
My blog
http://blogs.slcdug.org/petermorris/
|
|
| Back to top |
|
 |
Joanna Carter (TeamB) Guest
|
Posted: Sat Feb 05, 2005 12:37 pm Post subject: Re: Visitor pattern - *mehhh* |
|
|
"Peter Morris [Droopy eyes software]" <pete (AT) remove (DOT) this.droopyeyes.com> a
écrit dans le message de news: [email]4204acf5 (AT) newsgroups (DOT) borland.com[/email]...
| Quote: | Could you give me an example of how this is useful? Both determining I
mean. I see a single visitor as performing a single action.
|
The most important aspect of the Visitor pattern is the the 'double
despatch' that takes place between the Visitor and the Visitee.
All the 'Visitee' needs is to provide at the 'interface' level is an Accept
method that takes a generic Visitor.
The Visitor is passed to the Accept method (despatch 1) and then the
implementation fo the Visitee calls the method of the Visitor that is
specifically designed to take a parameter of the type of that member of the
hierarchy (despatch 2).
Visitors are meant to be extensions to the behaviour of a stable hierarchy;
as has been said before they are not intended to be used where the hierarchy
is liable to change.
By declaring a Visitor, you are not declaring a separate entity from the
hierarchy; both the hierarchy and the Visitor should be designed in concert.
This then gives you a hierarchy whose *behaviour* can be added to or
modified without change to its *structure*.
Gerrit mentions using a Visitor to parse lists of derived types, where the
same operation may be implemented differently, dependent on the type of the
list item.
e.g.
//////////////////////////////
public abstract class Visitor { } // sort of namespace marker ?
public abstract class Shape
{
public virtual void Accept(IVisitor visitor)
{
ShapeVisitor sv = (ShapeVisitor) visitor;
if (sv = null)
throw new Exception("Visitor not appropriate to this hierarchy");
}
}
public class Circle : Shape
{
public override void Accept(Visitor visitor)
{
try
{
base.Accept(visitor);
sv.VisitCircle(this);
}
catch Exception e
{
rethrow e;
}
}
}
public class Square : Shape
{
...
}
public class Polygon : Shape
{
...
}
public abstract class ShapeVisitor : Visitor
{
public abstract void VisitCircle(Circle obj);
public abstract void VisitSquare(Square obj);
public abstract void VisitPolygon(Polygon obj);
}
public class ShapeDrawingVisitor : ShapeVisitor
{
private Graphics canvas;
public ShapeDrawingVisitor(Graphics g)
{
canvas = g;
}
public abstract void VisitCircle(Circle obj);
{
// call GDI methods for drawing circle using g
}
public abstract void VisitSquare(Square obj);
{
// call GDI methods for drawing square using g
}
public abstract void VisitPolygon(Polygon obj);
{
// call GDI methods for drawing polygon using g
}
}
///////////////////////////
In this case the hierarchy is for a game and is never going to change;
however we may want to add different behaviour like 3D drawing instead of
2D. Instead of deriving another class from each member of the hierarchy just
to override the 2D drawing behaviour, we can simply write a
3DShapeDrawingVisitor and pass that to the existing Accept methods.
Voilà!! Instant inheritance of three classes by writing one Visitor.
Joanna
--
Joanna Carter (TeamB)
Consultant Software Engineer
TeamBUG support for UK-BUG
TeamMM support for ModelMaker
|
|
| Back to top |
|
 |
Joanna Carter (TeamB) Guest
|
Posted: Sat Feb 05, 2005 12:39 pm Post subject: Re: Visitor pattern - *mehhh* |
|
|
"Joanna Carter (TeamB)" <joanna (AT) nospam (DOT) co.uk> a écrit dans le message de
news: 4204bdf9$1 (AT) newsgroups (DOT) borland.com...
Corrigenda :
| Quote: | public virtual void Accept(IVisitor visitor)
|
...should be
public virtual void Accept(Visitor visitor)
See Jim, no interfaces )
Joanna
--
Joanna Carter (TeamB)
Consultant Software Engineer
TeamBUG support for UK-BUG
TeamMM support for ModelMaker
|
|
| Back to top |
|
 |
Jarle Stabell Guest
|
Posted: Sat Feb 05, 2005 12:55 pm Post subject: Re: Visitor pattern - *mehhh* |
|
|
Gerrit Beuze wrote:
| Quote: | A visitor is the implementatin of a matric VMT instead of vector
(linear) VMT.
Both the type of the visitor AND the type of the visited class
determine which
method gets called.
|
Yes, it is analoguous to doing single argument polymorphism (manually)
in a language not directly supporting it (like C). (But you get more
help from the compiler going from 1 to 2 dimensions compared to going
from 0 to 1.)
Some languages supports polymorphism on multiple parameters. In these
languages you don't need the visitor pattern.
Cheers,
Jarle
|
|
| Back to top |
|
 |
Peter Morris [Droopy eyes Guest
|
Posted: Sat Feb 05, 2005 1:52 pm Post subject: Re: Visitor pattern - *mehhh* |
|
|
| Quote: | To dismiss Visitor out of hand is to rob yourself of a useful, elegant
technique, though.
|
I'm not dismissing it, I'm just not impressed with the way the GOF shows it
in the diagrams (abstract methods etc). If I were dismissing it I wouldn't
have mentioned it :-)
|
|
| Back to top |
|
 |
Peter Morris [Droopy eyes Guest
|
Posted: Sat Feb 05, 2005 2:13 pm Post subject: Re: Visitor pattern - *mehhh* |
|
|
I think the problem I have with this pattern as I understood it from the
book was
1) The need for a method to be implemented in the visitor class for every
type of node
2) The fact that the node decides what action the visitor should take
I still don't like the fact that #1 is required, but I think the penny is
starting to drop for point #2.
Point #2 doesn't say "what" the visitor should do, it only tells it what
type of node it should perform its action upon. So, I can have a
PrintVisitor which can PrintCharacter PrintGraphic etc, and I can have a
SaveVisitor which streams each node to disk.
My problem was that I was seeing the node code like so
Visitor.VisitCharacter(Self)
and thinking that it was telling the visitor what to do, when in fact that
is just a way of eliminating the visitor from having to do this
if Node is CharacterNode then
DoCharacterWork(Node)
else
if Node is GraphicNode then
DoGraphicWork(Node);
Which approach you prefer is a balance between
A) Do I want a method for each type of node available
B) Can I trust myself to remember to add an "if" statement to every visitor
each time I add a new node type
I think I am starting to see the point now. The hardest part I suppose is
going to be seeing where in my everyday life I can utilise it.
--
Pete
====
ECO Modeler, Audio compression components, DIB graphics controls,
FastStrings
http://www.droopyeyes.com
Read or write articles on just about anything
http://www.HowToDoThings.com
My blog
http://blogs.slcdug.org/petermorris/
|
|
| Back to top |
|
 |
Peter Morris [Droopy eyes Guest
|
Posted: Sat Feb 05, 2005 2:38 pm Post subject: Re: Visitor pattern - *mehhh* |
|
|
*Plink*
That's the sound of the penny dropping.
1) The base visitor class will have an abstract method per node type
2) There is a method per node type so that the action being performed
(although the same action) is performed differently depending on the node
type
3) The action is determined by the type of descendant visitor, eg
PrintVisitor SaveVisitor
4) The methods are abstract to ensure that an error is thrown if ever you
add a new node type and forget to implement a handler for a specific node
type
To me the book doesn't make this 100% clear.
I have some code to write soon which handles lots of different types of
payments. I could use a visitor pattern for this, but if I see that there
is only going to be a single type of action "ProcessPaymentVisitor" is it
work using a visitor pattern? What if some of the payment types require
different rules when being processed, shouldn't the code really be a virtual
method of the base Payment class?
--
Pete
====
ECO Modeler, Audio compression components, DIB graphics controls,
FastStrings
http://www.droopyeyes.com
Read or write articles on just about anything
http://www.HowToDoThings.com
My blog
http://blogs.slcdug.org/petermorris/
|
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|