Jump to content

Talk:Visitor pattern: Difference between revisions

Page contents not supported in other languages.
From Wikipedia, the free encyclopedia
Content deleted Content added
SineBot (talk | contribs)
m Signing comment by 141.202.248.52 - ""
mNo edit summary
Line 1: Line 1:
{{WikiProject Java}}
{{WikiProject Java}}
{{WikiProject Computer science}}
{{WikiProject Computer science}}

I regret to say that this is one of the most opaque explanations I have ever read. It does not really explain it at all. Any article that you have to re-read to understand what it's trying to explain has failed.



Computer Science is bullshit. it does NOT exist. <span style="font-size: smaller;" class="autosigned">—Preceding [[Wikipedia:Signatures|unsigned]] comment added by [[Special:Contributions/141.202.248.52|141.202.248.52]] ([[User talk:141.202.248.52|talk]]) 13:44, 7 December 2009 (UTC)</span><!-- Template:UnsignedIP --> <!--Autosigned by SineBot-->
Computer Science is bullshit. it does NOT exist. <span style="font-size: smaller;" class="autosigned">—Preceding [[Wikipedia:Signatures|unsigned]] comment added by [[Special:Contributions/141.202.248.52|141.202.248.52]] ([[User talk:141.202.248.52|talk]]) 13:44, 7 December 2009 (UTC)</span><!-- Template:UnsignedIP --> <!--Autosigned by SineBot-->

Revision as of 11:25, 6 April 2010

WikiProject iconJava Unassessed
WikiProject iconThis article is within the scope of WikiProject Java, a collaborative effort to improve the coverage of Java on Wikipedia. If you would like to participate, please visit the project page, where you can join the discussion and see a list of open tasks.
???This article has not yet received a rating on Wikipedia's content assessment scale.
???This article has not yet received a rating on the project's importance scale.
WikiProject iconComputer science Unassessed
WikiProject iconThis article is within the scope of WikiProject Computer science, a collaborative effort to improve the coverage of Computer science related articles on Wikipedia. If you would like to participate, please visit the project page, where you can join the discussion and see a list of open tasks.
???This article has not yet received a rating on Wikipedia's content assessment scale.
???This article has not yet received a rating on the project's importance scale.
Things you can help WikiProject Computer science with:

I regret to say that this is one of the most opaque explanations I have ever read. It does not really explain it at all. Any article that you have to re-read to understand what it's trying to explain has failed.


Computer Science is bullshit. it does NOT exist. —Preceding unsigned comment added by 141.202.248.52 (talk) 13:44, 7 December 2009 (UTC)[reply]

codeproject.com link removal

I propose to remove codeproject.com from the Other links. It has 110 links on Wikipedia raising the issues of advertising, promotion, spamming, and just plain over-linking. Comments anyone?

--C# Online.NET Editor 14:51, 12 February 2007 (UTC)


I am adding problem statement to understand the design usability better.

In organization there are different kind of employees and consultants so you can make a hierarchies based upon employee profile. Like Payee->Employee-> Management, Worker, Engineer. Payee->Consultant->Management, Engineer. Each have its own salary and salary-structures and saving/saving-plans. We have some algorithms to calculate deduction from pay for each one. Now a software need to design to calculate different deductions from pay of different profile's payees. These algorithms can be in different class and those classes will be visitor class. And the software can follow the Visitor Design pattern for best design. --Akash Gupta --203.90.124.178 11:59, 22 Jun 2004 (UTC)

With regard to the above, the Visitor Pattern would be useful if you have an aggregate object representing the organization chart, and you want to traverse that chart, applying an algorithm to each element of the chart such that it's customized to the type of that element. The chart's accept() function provides a way to map through the chart, it's a function applicator. The visitor is the function to be applied to all parts of the chart.

If you have some conventional list of the employees, you don't need the accept() thing; you use some existing list iteration mechanism to "visit" all of these objects. Moreover, perhaps there is no need to split the algorithm away from the objects. You can perhaps just put the payment deduction into a function in the Employee class that can be overridden. Or, an Employee object can have a helper object that handles details of payroll.

for_each (employee in employee_list) {
  PayrollPolicy *policy = employee->getPayrollPolicy();
  policy->CalculateDeduction(employee);
}


Smalltalk single dispatch?

The 3rd paragraph states this: "... in a conventional single-dispatch object-oriented language such as Java , Smalltalk, and C++."

I'm not directly familiar with Smalltalk, but I've always thought that Smalltalk used double-dispatch.

Can anyone confirm that?


Well, I think I'd also heard that, but a simple look at SmallTalk shows it's not allowed by the sintax: You write an object (or an expression returning an object) and then a message to send to it ("method to invocate on it")... And define methods writing "Class>>Message: Argument[s]"... So unless something is ommited in the article, there's no way to define double-dispatch methods.
(Well, it's the first time I look at SmallTalk, so perhaps I'm wrong =) )
-- User:E-uyyn

Some poorness

Until we include in the article explicitly what problems this construct addresses, it will remain poor. I'm a not-very-stupid 5th degree Computer Science student and had to deduce by myself this pattern's utility: So I imagine the article will be worthless for the main reader.

I agree with this assessment. Once the usefulness finally occurred to me (no reflection needed to handle different types), I went to the Java and C# example and expected to see the VisitorDemo iterating over a bunch of objects of indeterminate type and not having to use reflection. Instead, VisitorDemo just creates a Car object. The Visitor pattern isn't even useful in this scenario. Can we please get some better examples, and some better code samples? ThePedanticPrick 15:53, 12 April 2006 (UTC)[reply]
Ok, wait, I get it now. Car.accept passes the visitor to the accept methods of its components. I should have read more carefully. Nevertheless, this highlights the need for more clarity on the page. Also, it seems like there's more than one benefit to using this. There's the simulation of double dispatch, or what I called "no reflection needed", there's the ability to let an object handle its own component composition (also something we need to explain more clearly), and what BenFrantzDale describes below, the ability to decouple the function from the objects. He also goes on to describe something I don't fully understand yet, not being a graphics guy. ThePedanticPrick 19:27, 12 April 2006 (UTC)[reply]
Two useful examples would be scene graphs and abstract syntax trees. Both often make use of the visitor pattern to traverse them. I think the Car example is good though, since it is pretty simple, but still illustrates its purpose. It is a complicated pattern, and its utility can be hard to see, but, trust me, once you need it, you'll get it right away. —Preceding unsigned comment added by 216.8.167.246 (talk) 20:20, 18 February 2009 (UTC)[reply]

Yes, please please please come up with a better example -- show the pattern saving you work and making things clearer, not multiplying the amount of code by 10x and making things harder to understand. The collection should be something that's a pain in the butt to traverse, like an incoming XML stream to be SAX-parsed. Another way to highlight the advantages of the pattern is to offer different traverses to the same visitor class(es), e.g. allow the client to apply an XPath filter to that XML traverse. -- 38.105.178.219 (talk) 06:10, 15 October 2009 (UTC)[reply]

As far as I understand:

The pattern addresses the application of a function object (≈closure for lispers) to each component of a composite object. Specifficaly, it is valuable when you are using multiple different functions this way in the same classes. The pattern effectively separates the code used to navigate through the composite object's structure (thus, dependant on the structure) from the code which may be applied to each component.

In the ""regular/easy way""? (I mean, without using this pattern... anybody has a better wording for that?), the navigation code would be included, redundantly, in the different function objects, maybe even intertwined with the "apllication code".

As always, the problem with code redundancy is not the repetition itself (((the code-replication problem was solved long ago by the mighty programming utils copy and paste, not to menction lisp macros ;) ))), but redundancy in code which is dependant on a decission, such as the composite objects' structure in this case. The problem arises when the decission (i.e., the structure of the object) changes: The same corrections would have to be made, again and again (because of the redundancy), across several functions in the code. This wastes programmers' time, which is very expensive, and is error-prone (as is every task requiring a human to do a search).

If redundant code isn't factored-out, the time expended in corrections would be completely lost should a new change in the decission be made.

As an additional benefit, the pattern not only eliminates the said redundancy, but also puts the navigation code in the composite class. It is desirable to have dependent code in the same module (class for O.O. languages) in which the decission they depend on is taken (in this case, the decission is the class structure, which is specified in the class declaration/definition), so it can be inmediately fetched if a change needs to be done.


Remarks, please? -- Euyyn

The use described in Design Patterns is to move a higherarchy of methods into one place so that adding more such methods doesn't get out of hand. For example, if you have objects that will be drawn on the screen, you could have a draw() method for each. However, if you wanted to then draw them to an OpenGL context, you would have to add draw_OpenGL() methods. Then if you wanted to draw to postscript, you'd add draw_PostScript(). Each of these additions would scatter platform-dependant drawing code across all the files of the class higherarchy. If you use the visitor pattern, you'd be adding a single class for each of these additions and that class would contain all of the platform-dependant code.
Furthermore, if you have objects in scenegraph, you might want to draw them in different orders depending on the nature of the drawing context. For example, in OpenGL you might want to draw all brick-textured objects together to avoid excess context switching. The OpenGL visitor could collect a list of things to draw, sorted by texture usage, and only then would you draw the scene. This could not be accomplished easily with a simple recursive draw() method. —BenFrantzDale 20:25, 28 November 2005 (UTC)[reply]

This article is the #1 Google result for "visitor design pattern" and "visitor pattern". It needs to get better. There's lots of vague language and the organization seems poor. Also as other people have noted, the code examples are not very illuminating. Gandalfgeek (talk) 23:12, 3 July 2008 (UTC) gandalfgeek[reply]


Some comments

Here are some things that might be of interest:

  • The biggest drawback of the Visitor Pattern seems to be that each visitor needs knowledge about every single Element implementation class. This becomes obvious when the Visitor implementation is compiled into a library but a user of this library has the ability to derive new element classes.
  • A Visitor might (or might not) contain a default implementation of visit(Element) for implementations not handled by special methods. The simplest Visitor implementation would thus just contain a single visit(Element) method that performs an action applicable to any implementation. In this scenario only the navigation through the hierarchy is factored out but concrete actions remain as methods in implementation classes (MyShape.draw() vs. MyShape.draw_OpenGL()).
  • In more complex scenarios, these actions may be moved into the visitor. MyShape.draw() is replaced by DrawVisitor.visit(MyShape), MyShape.draw_OpenGL() is replaced by OpenGLDrawVisitor.visit(MyShape).
  • A full-fledged Visitor implementation would contain two callback methods for every concrete implementation class, because some actions might have to be taken before aggregate elements are visited and some after. The accept() methods in concrete Elements would then do visitor.startVisit(this); call accept(visitor) on each aggregate element; visitor.endVisit(this). —Preceding unsigned comment added by 78.54.7.83 (talk) 10:45, 19 July 2008 (UTC)[reply]

Poor sample If a reader looks at the Java example, he/she might ask: what do we need the double-dispatch for here? And actually double-dispatch is only needed if there would be inheritance in the class hierarchy or at least the iteration of the subobjects is performed in the classes themself, not the visitor. So in effect, the example is very poor. —Preceding unsigned comment added by 195.243.100.246 (talk) 14:39, 18 June 2009 (UTC)[reply]

UML

I've created and added a UML diagram to the article. Let me know if its semi-decent. I come from a Java/C++ background, so I don't know if the diagram applies to the other languages as ubiquitously as I've assumed. I also tried uploading the original .dia file so the image my be edited easily if needed, but it complained that it wasn't a recommended image format :-/ If you want it, just drop me a line. It's not rocket-science, but it does take some time to do all that clicking. The Extremist 10:08, 9 December 2005 (UTC)[reply]

please post it here so that we can discuss it :-) MatthieuWiki 17:26, 13 July 2007 (UTC)[reply]

Examples

These examples have gotten out of hand. There need only be one example of this; more than one adds nothing about the pattern itself. I propose it get cut back to just one of the Java, C++, Python, or C# example, and the rest get moved to Wikibooks. Thoughts? —Ben FrantzDale 03:16, 22 May 2006 (UTC)[reply]

Where is it?

I'm not sure who wrote the previous paragraph ("Where is it?"), and at first I wasn't even sure what it meant, but now I understand: There used to be more examples and now some are gone, allegedly because only Java is needed. But if the others were moved, to where were they moved? I join in asking that question. There is a broken (i.e., red) link to visitor pattern examples. Also, there's more than Java there now: there's C++, either because it was not removed or because it has crept back in. If there are going to be multiple languages, let's please restore the Lisp, too. If there aren't, and it's because everyone can read Java, then what is C++ needed for? Although I don't especially like Java, I can live with it as the exemplar if the justification is that there should be only one language and that the first example offered was in that language. Recall that, like it or not, this is more than just documentation: the presence of extra languages here becomes a de facto advertisement for the "preferred" languages and invites every language in the universe to offer its version once a pattern has been shown (sorry for the bad pun). It would seem best not to start down that path. Netsettler (talk) 02:23, 28 March 2008 (UTC)[reply]

Examples should show a single implementation from various classes of languages. E.g. the Java and C++ examples show really the same thing, modulo some syntax, which is not surprising as they are both statically typed OO languages. There should also be an example from a dynamic language like Python, Ruby, or Javascript, as well as a discussion of how the pattern implementation is significantly different in dynamic languages due to the use of reflection (e.g. the compiler.ast module in Python 2.5 has an idiomatic implementation of the visitor pattern). —Preceding unsigned comment added by Gandalfgeek (talkcontribs) 04:53, 29 June 2008 (UTC)[reply]

These examples are still out of hand. I still think Java is the best example language for this since it is strongly typed and has garbage collection so all of the pertinent details are explicit and all of the nuisance things (memory allocation) aren't needed. (FWIW, I'm not a fan of Java; I barely know Java and like C++ and Python.) If it weren't for the fact that strong typing goes well with OO design patterns, I'd go for Python for its similarity to pseudocode, but I think things would be lost for this example without typed arguments and without proper abstract base class. At the risk of re-starting an edit war, I'm going to be bold. —Ben FrantzDale (talk) 00:55, 8 April 2009 (UTC)[reply]

How about a more realistic example...?

These car examples are all well and good, but fail to recognize any of the difficulties that instantly come up when traversing, say, source code or object code. I've done this several times now and the visitor pattern just doesn't seem to be reducing overhead for me - it still needs to be essentially re-written for each use, and in general causes about as many problems as it solves. I'd be interested in other people's thoughts... Dan Knapp 16:47, 24 May 2006 (UTC)[reply]

You might want to take a look at "Visitor Combination and Traversal Control" by Joost Visser - it specifically addresses visitor reuse and traversal strategies. 129.34.20.19 14:22, 29 June 2006 (UTC)[reply]
The key is that it gives you double dispatch. This essentially allows you to add virtual methods to classes that already exist. As a result, you get the power of virtual methods without having to add bloat to the classes. Plus it localizes a particular algorithm to one class rather than scattering it across classes.
For example, suppose I have a scenegraph with heterogenious objects that I want to draw. One way to draw this would be to have each object inherit some drawable class and implement draw(). But now suppose want to draw it to both OpenGL and DirectX. I'd probably put in a draw_OpenGL() and a draw_DirectX() method. This wouldn't scale well to more drawing modes, would spread platform-specific code across all drawable objects, and would put drawing code in classes which fundamentally represent drawable things, not ways to draw them. (That is, a cube is a cube even if it doesn't know anything about OpenGL.)
In contrast, using the visitor pattern the OpenGL drawing functions would all be in one file, the DirectX functions in another file, and adding another drawing mode would simply be a matter of adding a third file.
Does that help? —Ben FrantzDale 22:25, 27 July 2006 (UTC)[reply]
Yes, that tells me a lot more about what one is supposed to gain from the pattern. I'm used to languages that have multimethods anyway, which was part of why I didn't get it.
I do feel compelled to point out that realistically, it's unlikely one would want a third 3D API, but the separation you describe is definitely a good thing.
Thanks. Dan Knapp 03:50, 8 November 2006 (UTC)[reply]

A simple example that I recently implemented involved parsing wikicode. Once the wikicode had been parsed into various elements like WikiText, WikiLink etc, Visitors can be coded to output to different formats, e.g. html or plaintext.

class HTMLVisitor
{
  public function visit(WikiLink $l)
  {
    echo "<a href=\"" . htmlentities($l->url()) . "\">" . $l->text() . "</a>";
  }
  // ...
}
class PlainTextVisitor
{
  public function visit(WikiLink $l)
  {
    echo $l->text() . " [" . $l->url() . "]";
  }
  // ...
}

I think the visitor pattern works well for translating an intermediate format into some external format. This is why compilers use visitors to generate machine instructions for different architectures.84.66.221.139 23:23, 11 December 2006 (UTC)[reply]

Can any one add the compiler example as it is coming in discussion so many times. For the end user it is good to know the very basic implementation of this pattern. or any link to it would be great. —Preceding unsigned comment added by 203.91.193.7 (talk) 10:01, 30 March 2009 (UTC)[reply]

Multimethod

Multimethod is a more general term for "double dispatch"

Car class in example is wrong?

As I understand the visitor pattern, shouldn't the the Accept method of the Car class call Accept on the Body, Engine, and all the wheels? —The preceding unsigned comment was added by 217.166.1.202 (talk) 10:20, 26 February 2007 (UTC).[reply]

I agree with the above comment. The responsibility of a visitable object is to provide navigation to sub-components. A visitor would only be interested in adding extended functionality to individual nodes and should not be aware of how the nodes are linked. This is the responsibility of the aggregated object itself. --Pav007 11:53, 14 March 2007 (UTC)[reply]
I don't have Patterns in front of me, but I believe it can go either way. —Ben FrantzDale 12:05, 14 March 2007 (UTC)[reply]
It does seem to contradict the article though, which says: "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". Now, this implies that things can be different (needs clarification, by the way), but one would hope that the example should demonstrate the "simplest version" anyway. So i'm going ahead and fixing it. -- int19h 17:24, 15 March 2007 (UTC)[reply]
The article also says that: "the visitor design pattern is a way of separating an algorithm from an object structure upon which it operates". So, according to this, visitor is the algorithm (operations), and the car is the structure upon which it operates. The point is that algorithm (the visitor) should not know about the structure. Thus, the car should define its structure for the algorithm, and the initial call visitor.visitCar(car) should be replaced with car.accept(visitor), where car should call accept(visitor) for each its element. —Preceding unsigned comment added by 77.123.143.133 (talk) 07:20, 26 November 2008 (UTC)[reply]
It is true that the visitor pattern can be interpreted in both of these ways (I prefer a visitable Car which calls accept on its parts). But implementing the traversal in each of the Visitors is a clear violation of the DRY principle. If the car stays not beeing visitable, there should at least be an ordinary function (not member) VisitCar which implements the traversal. —Preceding unsigned comment added by 93.190.250.146 (talk) 07:58, 22 July 2009 (UTC)[reply]

Duplicate examples in different languages

Given that the D example adds nothing that the Java one doesn't, I'm removing it. All it does is illustrate the D language, which isn't the point of this particular article. Matthew0028 02:58, 19 June 2007 (UTC)[reply]

C++ example is badly coded

In my opinion the c++ code example needs to be rewritten or removed. Some issues: There's member variables in the public interface, the classes are guaranteed to leak since there's no destructors and no way to obtain pointers to the dynamically allocated memory. The Microsoft-specific "stdafx.h" precompiled header file is included. Const-correctness is not exercised. std::string objects are passed by value. A temporary iterator is stored as a member. "using namespace std;" is placed is one of the header files. I have put up a cleanup template for now. Ufretin 15:17, 3 October 2007 (UTC)[reply]

Callback

It seems like there should be some mention of callback, http://en.wikipedia.org/wiki/Callback_%28computer_science%29, or just a link to it somewhere within this article.

Unencyclopedic language

I'm not sure if expressions like "If I have a bunch of classes" are very encyclopedic. Also things like "The idea is to" is something you might see in a newsgroup post or an informal publication, not in an encyclopedia. Wopr (talk) 14:46, 9 March 2008 (UTC)[reply]

Similar concept?

please fixme if i am wrong. i was thinking about it and i realized that this "visitor" thing can mean "unification". color_t blue, red, green; // initialize somewhere draw_dot(red); // each color is different "object"

// versus enum color_e { BLUE, RED, GREEN, NUM_COLORS }; color_t color[NUM_COLORS]; draw_dot(color[RED]); // colors are accesed as one *color this example shows that colors are unified. but visitor is more about hoisting up same behaviour, which changes with given context. i'd like to have it clear. Xchmelmilos (talk) 18:27, 11 March 2008 (UTC)[reply]

Example doesn't match narrative

The narrative indicates that the Car class would call the visitor on itself, and then on each of its elements (thus the visitor need not know the structure of the Car class). The example as coded does not reflect this structure, thus providing a misleading example. I can correct this, but will first just serve notice and let the original example provider correct the code if he/she is so inclined. NedHorvath 06:42, 20 September 2008 (UTC)[reply]

NULL vs 0

Prefer NULL over 0, because:

The C++ standard doesn't guarantees that a null pointer is equal to 0. Using 0 would make you code not fully portable. Since NULL is a macro you could (re)define it ones, rater than having to edit you code in many many places.

C++ Standard 18.1.4:

The macro NULL is an implementation-defined C++ null pointer constant in this Internnational Standard (4.10).

Footnote says:

Possible definitions include 0 and OL, but not (void*)0.

The C standard 6.3.2.3, paragraph 4 says:

Conversion of a null pointer to another pointer type yields a null pointer of that type. Any two null pointers shall compare equal.

If the intent was that programmer used 0 instead of NULL, then it isn't logical that they consistently use the word null. Also using NULL for pointers, and only for pointers, makes your code more readable. Or as Linus[1] put it

The fact is, people who write "0" are either living in the stone age, or are not sure about the type. "0" is an _integer_.

Wikipedia also warns users NULL null pointers can be non-0.

A null pointer has a reserved value, often but not necessarily the value zero, indicating that it refers to no object.

--Alfatrion (talk) 00:29, 7 April 2009 (UTC)[reply]

I agree with the logic of using NULL for abstract functions but I think it's nonstandard. Will it work with nullptr in C++0x, do you know? That is, could you do
virtual void foo() = nullptr; // or = NULL with a #define on NULL.
—Ben FrantzDale (talk) 01:07, 7 April 2009 (UTC)[reply]

Using NULL as a pure-specifier

Whether 0 or NULL should be used where a null pointer constant is required is a style issue, and open to debate. It is however, irrelevant to the changes being made to the example source code, because a pure-specifier has nothing to do with pointers at all. The claim that 0 would not be fully portable is exactly backwards.

As stated in my initial edit summary, a pure-specifier shall always be " = 0".[1] On certain implementations, "= NULL" may compile, if NULL happens to be #defined to 0, It may just as well be #defined to (2-2) or 0L,[2][3] which is not a valid pure-specifier, even if some compilers may (erroneously) accept 0L or 0x0.[4][1] Again, a pure-specifier is completely unrelated to null pointer constants, and must be " = 0". decltype (talk) 08:55, 7 April 2009 (UTC)[reply]

Your ride. I viewed it as an pointer and thats not quite ride. --Alfatrion (talk) 17:15, 7 April 2009 (UTC)[reply]
  1. ^ a b ISO/IEC (2003). ISO/IEC 14882:2003(E): Programming Languages - C++ §9.2 Class members [class.mem]
  2. ^ ISO/IEC (2003). ISO/IEC 14882:2003(E): Programming Languages - C++ §18.1 Types [lib.support.types] para. 4
  3. ^ ISO/IEC (2003). ISO/IEC 14882:2003(E): Programming Languages - C++ §4.10 Pointer conversions [conv.ptr] para. 1
  4. ^ http://gcc.gnu.org/ml/gcc-patches/2004-02/msg02318.html