📄 tij0180.html
字号:
<html><body>
<table width="100%"><tr>
<td>
<a href="http://www.bruceeckel.com/javabook.html">Bruce Eckel's Thinking in Java</a>
</td>
<td align="right">
<a href="tij_c.html">Contents</a> | <a href="tij0179.html">Prev</a> | <a href="tij0181.html">Next</a>
</td>
</tr></table>
<hr>
<H2 ALIGN=LEFT>
The
“visitor” pattern
</H2>
<DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">Now
consider applying a design pattern with an entirely different goal to the
trash-sorting problem.
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">For
this pattern, we are no longer concerned with optimizing the addition of new
types of
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Trash
</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">to
the system. Indeed, this pattern makes adding a new type of
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Trash
</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><I>more</I></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
complicated. The assumption is that you have a primary class hierarchy that is
fixed; perhaps it’s from another vendor and you can’t make changes
to that hierarchy. However, you’d like to add new polymorphic methods to
that hierarchy, which means that normally you’d have to add something to
the base class interface. So the dilemma is that you need to add methods to the
base class, but you can’t touch the base class. How do you get around this?
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">The
design pattern that solves this kind of problem is called a
“visitor” (the final one in the
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><I>Design
Patterns
</I></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
book), and it builds on the double
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><I>
</I></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">dispatching
scheme shown in the last section.
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">The
<A NAME="Index2999"></A><A NAME="Index3000"></A>visitor
pattern allows you to extend the interface of the primary type by creating a
separate class hierarchy of type
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Visitor
</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">to
virtualize the operations performed upon the primary type. The objects of the
primary type simply “accept” the visitor, then call the
visitor’s dynamically
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>-</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">bound
method. It looks like this:
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">Now,
if
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>v</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
is a
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Visitable
</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">handle
to an
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Aluminum</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
object, the code:
</FONT><P></DIV><DIV ALIGN=LEFT><TT><FONT FACE="Courier New" SIZE=3 COLOR="Black">PriceVisitor
pv = new PriceVisitor();
</FONT></TT><P><TT><FONT FACE="Courier New" SIZE=3 COLOR="Black">v.accept(pv);</FONT></TT><P></DIV><DIV ALIGN=LEFT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">causes
two polymorphic method calls: the first one to select
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Aluminum</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">’s
version of
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>accept( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">,
and the second one within
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>accept( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
when the specific version of
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>visit( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
is called dynamically using the base-class
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Visitor</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
handle
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>v</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">.</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">This
configuration means that new functionality can be added to the system in the
form of new subclasses of
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Visitor</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">.
The
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Trash
</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">hierarchy
doesn’t need to be touched. This is the prime benefit of the visitor
pattern: you can add new polymorphic functionality to a class hierarchy without
touching that hierarchy (once the
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>accept( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
methods have been installed). Note that the benefit is helpful here but not
exactly what we started out to accomplish, so at first blush you might decide
that this isn’t the desired solution.
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">But
look at one thing that’s been accomplished: the visitor solution avoids
sorting from the master
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Trash</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
sequence into individual typed sequences. Thus, you can leave everything in the
single master sequence and simply pass through that sequence using the
appropriate visitor to accomplish the goal. Although this behavior seems to be
a side effect of visitor, it does give us what we want (avoiding RTTI).
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">The
<A NAME="Index3001"></A><A NAME="Index3002"></A>double
dispatching in the visitor pattern takes care of determining both the type of
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Trash
</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">and
the type of
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Visitor</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">.</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>
</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">In
the following example, there are two implementations of
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Visitor</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">:
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>PriceVisitor</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
to both determine and sum the price, and
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>WeightVisitor</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
to keep track of the weights.
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">You
can see all of this implemented in the new, improved version of the recycling
program. As with
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>DoubleDispatch.java</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">,
the
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Trash</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
class is left alone and a new interface is created to add the
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>accept( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
method:
</FONT><P></DIV>
<font color="#990000"><PRE><font color="#009900">//: Visitable.java</font>
<font color="#009900">// An interface to add visitor functionality to </font>
<font color="#009900">// the Trash hierarchy without modifying the </font>
<font color="#009900">// base class.</font>
<font color="#0000ff">package</font> c16.trashvisitor;
<font color="#0000ff">import</font> c16.trash.*;
<font color="#0000ff">interface</font> Visitable {
<font color="#009900">// The new method:</font>
<font color="#0000ff">void</font> accept(Visitor v);
} <font color="#009900">///:~ </PRE></font></font><DIV ALIGN=LEFT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">The
subtypes of
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Aluminum</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">,</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>
Paper
</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">,</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>
Glass,
</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">and</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>
Cardboard
</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
implement the
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>accept( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
method:
</FONT><P></DIV>
<font color="#990000"><PRE><font color="#009900">//: VAluminum.java</font>
<font color="#009900">// Aluminum for the visitor pattern</font>
<font color="#0000ff">package</font> c16.trashvisitor;
<font color="#0000ff">import</font> c16.trash.*;
<font color="#0000ff">public</font> <font color="#0000ff">class</font> VAluminum <font color="#0000ff">extends</font> Aluminum
<font color="#0000ff">implements</font> Visitable {
<font color="#0000ff">public</font> VAluminum(<font color="#0000ff">double</font> wt) { <font color="#0000ff">super</font>(wt); }
<font color="#0000ff">public</font> <font color="#0000ff">void</font> accept(Visitor v) {
v.visit(<font color="#0000ff">this</font>);
}
} <font color="#009900">///:~ </PRE></font></font><DIV ALIGN=LEFT><P></DIV>
<font color="#990000"><PRE><font color="#009900">//: VPaper.java</font>
<font color="#009900">// Paper for the visitor pattern</font>
<font color="#0000ff">package</font> c16.trashvisitor;
<font color="#0000ff">import</font> c16.trash.*;
<font color="#0000ff">public</font> <font color="#0000ff">class</font> VPaper <font color="#0000ff">extends</font> Paper
<font color="#0000ff">implements</font> Visitable {
<font color="#0000ff">public</font> VPaper(<font color="#0000ff">double</font> wt) { <font color="#0000ff">super</font>(wt); }
<font color="#0000ff">public</font> <font color="#0000ff">void</font> accept(Visitor v) {
v.visit(<font color="#0000ff">this</font>);
}
} <font color="#009900">///:~ </PRE></font></font><DIV ALIGN=LEFT><P></DIV>
<font color="#990000"><PRE><font color="#009900">//: VGlass.java</font>
<font color="#009900">// Glass for the visitor pattern</font>
<font color="#0000ff">package</font> c16.trashvisitor;
<font color="#0000ff">import</font> c16.trash.*;
<font color="#0000ff">public</font> <font color="#0000ff">class</font> VGlass <font color="#0000ff">extends</font> Glass
<font color="#0000ff">implements</font> Visitable {
<font color="#0000ff">public</font> VGlass(<font color="#0000ff">double</font> wt) { <font color="#0000ff">super</font>(wt); }
<font color="#0000ff">public</font> <font color="#0000ff">void</font> accept(Visitor v) {
v.visit(<font color="#0000ff">this</font>);
}
} <font color="#009900">///:~ </PRE></font></font><DIV ALIGN=LEFT><P></DIV>
<font color="#990000"><PRE><font color="#009900">//: VCardboard.java</font>
<font color="#009900">// Cardboard for the visitor pattern</font>
<font color="#0000ff">package</font> c16.trashvisitor;
<font color="#0000ff">import</font> c16.trash.*;
<font color="#0000ff">public</font> <font color="#0000ff">class</font> VCardboard <font color="#0000ff">extends</font> Cardboard
<font color="#0000ff">implements</font> Visitable {
<font color="#0000ff">public</font> VCardboard(<font color="#0000ff">double</font> wt) { <font color="#0000ff">super</font>(wt); }
<font color="#0000ff">public</font> <font color="#0000ff">void</font> accept(Visitor v) {
v.visit(<font color="#0000ff">this</font>);
}
} <font color="#009900">///:~ </PRE></font></font><DIV ALIGN=LEFT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">Since
there’s nothing concrete in the
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Visitor</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
base class, it can be created as an
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>interface</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">:</FONT><P></DIV>
<font color="#990000"><PRE><font color="#009900">//: Visitor.java</font>
<font color="#009900">// The base interface for visitors</font>
<font color="#0000ff">package</font> c16.trashvisitor;
<font color="#0000ff">import</font> c16.trash.*;
<font color="#0000ff">interface</font> Visitor {
<font color="#0000ff">void</font> visit(VAluminum a);
<font color="#0000ff">void</font> visit(VPaper p);
<font color="#0000ff">void</font> visit(VGlass g);
<font color="#0000ff">void</font> visit(VCardboard c);
} <font color="#009900">///:~ </PRE></font></font><DIV ALIGN=LEFT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">Once
again custom
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Trash</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -