1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
|
<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<meta name="Author" content="Daniel Bonniot">
<meta name="GENERATOR" content="Mozilla/4.76 [en] (X11; U; OSF1 V4.0 alpha) [Netscape]">
<meta name="Description" content="Shows how multi-methods advantageously replace the visitor pattern.">
<meta name="Keywords" content="visitor,pattern,multi-method,multimethod,multiple dispatch">
<title>Visitor pattern considered useless</title>
<link rel=stylesheet href="style.css">
</head>
<body text="#000000" bgcolor="#FFFFFF" link="#0000EF" vlink="#51188E" alink="#FF0000">
<center>
<h1>
Visitor Pattern Versus Multimethods</h1></center>
<ol>
<h2>
The Visitor Pattern</h2>
The visitor pattern is a programming pattern that has been advocated strongly
for writing code operating on a hierarchy of classes. <a href="http://www.ti.et-inf.uni-siegen.de/Entwurfsmuster/ArtikelImport/osefa/patterns/visitor/intent.htm">A
thorough description is available there</a> from the <i>Design Patterns</i>
book.
<p>A typical example is the definition of operations on an Abstract Syntax
Tree. Here is Java code using a Visitor:
<PRE>
<FONT COLOR="#0000ee">package</FONT> syntax;
<FONT COLOR="#0000ee">abstract</FONT> <FONT COLOR="#0000ee">class</FONT> <FONT COLOR="#cd0000">ExpressionVisitor</FONT>
{
<FONT COLOR="#0000ee">abstract</FONT> <FONT COLOR="#b7860b">void</FONT> visitIntExp(<FONT COLOR="#b7860b">IntExp </FONT>e);
<FONT COLOR="#0000ee">abstract</FONT> <FONT COLOR="#b7860b">void</FONT> visitAddExp(<FONT COLOR="#b7860b">AddExp </FONT>e);
}
<FONT COLOR="#0000ee">abstract</FONT> <FONT COLOR="#0000ee">class</FONT> <FONT COLOR="#cd0000">Expression</FONT>
{
<FONT COLOR="#0000ee">abstract</FONT> <FONT COLOR="#b7860b">void</FONT> accept(<FONT COLOR="#b7860b">ExpressionVisitor </FONT>v);
}
<FONT COLOR="#0000ee">class</FONT> <FONT COLOR="#cd0000">IntExp</FONT> <FONT COLOR="#0000ee">extends</FONT> <FONT COLOR="#b7860b">Expression </FONT>
{
<FONT COLOR="#b7860b">int</FONT> <FONT COLOR="#cd0000">value</FONT>;
<FONT COLOR="#b7860b">void</FONT> <FONT COLOR="#cd0000">accept</FONT>(<FONT COLOR="#b7860b">ExpressionVisitor </FONT>v)
{
v.visitIntExp(<FONT COLOR="#8080ff">this</FONT>);
}
}
<FONT COLOR="#0000ee">class</FONT> <FONT COLOR="#cd0000">AddExp</FONT> <FONT COLOR="#0000ee">extends</FONT> <FONT COLOR="#b7860b">Expression </FONT>
{
<FONT COLOR="#b7860b">Expression </FONT>e1, e2;
<FONT COLOR="#b7860b">void</FONT> <FONT COLOR="#cd0000">accept</FONT>(<FONT COLOR="#b7860b">ExpressionVisitor </FONT>v)
{
v.visitAddExp(<FONT COLOR="#8080ff">this</FONT>);
}
}
</PRE>
The interest of this construction is that
it is now possible to define operations on expressions
by subclassing ExpressionVisitor.
This can even be done in a different package,
without modifying the expression hierarchy classes.
<PRE>
<FONT COLOR="#008b00">// Behaviour can now be defined on Expressions
</FONT>
<FONT COLOR="#0000ee">package</FONT> tools;
<FONT COLOR="#0000ee">class</FONT> <FONT COLOR="#cd0000">PrettyPrint</FONT> <FONT COLOR="#0000ee">extends</FONT> <FONT COLOR="#b7860b">ExpressionVisitor </FONT>
{
<FONT COLOR="#b7860b">void</FONT> <FONT COLOR="#cd0000">visitIntExp</FONT>(<FONT COLOR="#b7860b">IntExp </FONT>e)
{
<FONT COLOR="#b7860b">System</FONT>.out.print(e.value);
}
<FONT COLOR="#b7860b">void</FONT> <FONT COLOR="#cd0000">visitAddExp</FONT>(<FONT COLOR="#b7860b">AddExp </FONT>e)
{
e.e1.accept(<FONT COLOR="#8080ff">this</FONT>);
<FONT COLOR="#b7860b">System</FONT>.out.print(<FONT COLOR="#a020f0">" + "</FONT>);
e.e2.accept(<FONT COLOR="#8080ff">this</FONT>);
}
}</PRE>
Without visitors, the classes have to be modified each time a new
operations is added.
In this case, a <tt>prettyPrint</tt> member method
would be added to each class.
<p>
Another possibility would be to define a static method
in the new package. But then it would be necessary to test
the argument with <tt>instanceof</tt> and use downcasts.
In short, lose the benefits of object-orientation.
<br>
<h2>
Shortcomings of the Visitor pattern</h2>
However, the Visitor pattern has serious flaws:
<ol>
<li>
An obvious problem is that the arguments and the return type of visiting
methods have to be known in advance. In this example, to define a <tt>prettyPrint</tt>
function that returns a <tt>String</tt>, a new Visitor class has to be
defined, as well as a new <tt>accept</tt> method <b>in every class of the
hierarchy</b>. And the same job has to be done again to define an evaluation
function that returns an integer. The same problem occurs if the visiting
methods needs parameters.</li>
<li>
The code is more obscure. In particular with recursive calls: in <tt>e.e1.accept(this)</tt>
one cannot see that the call is indeed a prettyprint. One would expect
<tt>prettyPrint(e.e1)</tt> there.</li>
<li>
A lot of code has to be written to prepare the use of visitors: the visitor
class with one abstract method per class, and a accept method per class.
This code is tedious and boring to write.
If we add a new class, the visitor class needs a new method. Furthermore,
it is indeed likely that a new visiting method will need the definition
of a new visitor pattern, as seen in point 1. At the least, several patterns
have often to be written.</li>
<li>
If a visitor pattern has not been written in the first time, the hierarchy
has to be modified to implement it. In particular, if the hierarchy cannot
be modified because you are not allowed to, the visitor pattern cannot
be applied at all.</li>
</ol>
<h2>
Alternative solution with Multimethods</h2>
Here is the same example, using multi-methods,
in <a href="index.html"><font face="Comic Sans MS">Nice</font></a>.
As you can see, it is much
shorter and natural, and it solves all the problems above.
<p>
<PRE>
<FONT COLOR="#0000ee">package</FONT> syntax;
<FONT COLOR="#0000ee">abstract</FONT> <FONT COLOR="#0000ee">class</FONT> <FONT COLOR="#cd0000">Expression</FONT> { }
<FONT COLOR="#0000ee">class</FONT> <FONT COLOR="#cd0000">IntExp</FONT> <FONT COLOR="#0000ee">extends</FONT> <FONT COLOR="#b7860b">Expression </FONT>
{
<FONT COLOR="#b7860b">int</FONT> <FONT COLOR="#cd0000">value</FONT>;
}
<FONT COLOR="#0000ee">class</FONT> <FONT COLOR="#cd0000">AddExp</FONT> <FONT COLOR="#0000ee">extends</FONT> <FONT COLOR="#b7860b">Expression </FONT>
{
<FONT COLOR="#b7860b">Expression </FONT>e1, e2;
}
<FONT COLOR="#008b00">// Behaviour can now be defined on Expressions
</FONT>
<FONT COLOR="#0000ee">package</FONT> tools;
<FONT COLOR="#b7860b">void</FONT> <FONT COLOR="#cd0000">prettyPrint</FONT>(<FONT COLOR="#b7860b">Expression </FONT>e);
<FONT COLOR="#cd0000">prettyPrint</FONT>(<FONT COLOR="#b7860b">IntExp</FONT> e)
{
<FONT COLOR="#b7860b">System</FONT>.out.print(e.value);
}
<FONT COLOR="#cd0000">prettyPrint</FONT>(<FONT COLOR="#b7860b">AddExp</FONT> e)
{
prettyPrint(e.e1);
<FONT COLOR="#b7860b">System</FONT>.out.print(<FONT COLOR="#a020f0">" + "</FONT>);
prettyPrint(e.e2);
}
</PRE>
<h2>
Comparison of the two approaches</h2>
Multi-methods allow to solve the situation at which the Visitor pattern
aims, without carrying its disadvantages:
<ol>
<li>
There is no need to write preliminary code to "prepare the way" for visitors.
This solves points 1, 2 and 4 above.</li>
<li>
The code is shorter and more natural. Recursive calls appear as such.</li>
</ol>
<p><br>The Visitor pattern is a trick to introduce multiple dispatch in
a language that lacks it. However, it raises serious issues. Language support
for multi-methods makes it much easier and elegant to handle the common
situation where the Visitor pattern applies.
<br>
<h2>
General information about <font face="Comic Sans MS">Nice</font> :
the <a href="index.html">Nice home page</a></h2>
</ol>
</body>
</html>
|