📄 chapter16.html
字号:
<font color=#0000ff>abstract</font> <font color=#0000ff>double</font> value();
<font color=#0000ff>double</font> weight() { <font color=#0000ff>return</font> weight; }
<font color=#009900>// Sums the value of Trash in a bin:</font>
<font color=#0000ff>static</font> <font color=#0000ff>void</font> sumValue(Vector bin) {
Enumeration e = bin.elements();
<font color=#0000ff>double</font> val = 0.0f;
<font color=#0000ff>while</font>(e.hasMoreElements()) {
<font color=#009900>// One kind of RTTI:</font>
<font color=#009900>// A dynamically-checked cast</font>
Trash t = (Trash)e.nextElement();
<font color=#009900>// Polymorphism in action:</font>
val += t.weight() * t.value();
System.out.println(
<font color=#004488>"weight of "</font> +
<font color=#009900>// Using RTTI to get type</font>
<font color=#009900>// information about the class:</font>
t.getClass().getName() +
<font color=#004488>" = "</font> + t.weight());
}
System.out.println(<font color=#004488>"Total value = "</font> + val);
}
}
<font color=#0000ff>class</font> Aluminum <font color=#0000ff>extends</font> Trash {
<font color=#0000ff>static</font> <font color=#0000ff>double</font> val = 1.67f;
Aluminum(<font color=#0000ff>double</font> wt) { <font color=#0000ff>super</font>(wt); }
<font color=#0000ff>double</font> value() { <font color=#0000ff>return</font> val; }
<font color=#0000ff>static</font> <font color=#0000ff>void</font> value(<font color=#0000ff>double</font> newval) {
val = newval;
}
}
<font color=#0000ff>class</font> Paper <font color=#0000ff>extends</font> Trash {
<font color=#0000ff>static</font> <font color=#0000ff>double</font> val = 0.10f;
Paper(<font color=#0000ff>double</font> wt) { <font color=#0000ff>super</font>(wt); }
<font color=#0000ff>double</font> value() { <font color=#0000ff>return</font> val; }
<font color=#0000ff>static</font> <font color=#0000ff>void</font> value(<font color=#0000ff>double</font> newval) {
val = newval;
}
}
<font color=#0000ff>class</font> Glass <font color=#0000ff>extends</font> Trash {
<font color=#0000ff>static</font> <font color=#0000ff>double</font> val = 0.23f;
Glass(<font color=#0000ff>double</font> wt) { <font color=#0000ff>super</font>(wt); }
<font color=#0000ff>double</font> value() { <font color=#0000ff>return</font> val; }
<font color=#0000ff>static</font> <font color=#0000ff>void</font> value(<font color=#0000ff>double</font> newval) {
val = newval;
}
}
<font color=#0000ff>public</font> <font color=#0000ff>class</font> RecycleA {
<font color=#0000ff>public</font> <font color=#0000ff>static</font> <font color=#0000ff>void</font> main(String[] args) {
Vector bin = <font color=#0000ff>new</font> Vector();
<font color=#009900>// Fill up the Trash bin:</font>
<font color=#0000ff>for</font>(<font color=#0000ff>int</font> i = 0; i < 30; i++)
<font color=#0000ff>switch</font>((<font color=#0000ff>int</font>)(Math.random() * 3)) {
<font color=#0000ff>case</font> 0 :
bin.addElement(<font color=#0000ff>new</font>
Aluminum(Math.random() * 100));
<font color=#0000ff>break</font>;
<font color=#0000ff>case</font> 1 :
bin.addElement(<font color=#0000ff>new</font>
Paper(Math.random() * 100));
<font color=#0000ff>break</font>;
<font color=#0000ff>case</font> 2 :
bin.addElement(<font color=#0000ff>new</font>
Glass(Math.random() * 100));
}
Vector
glassBin = <font color=#0000ff>new</font> Vector(),
paperBin = <font color=#0000ff>new</font> Vector(),
alBin = <font color=#0000ff>new</font> Vector();
Enumeration sorter = bin.elements();
<font color=#009900>// Sort the Trash:</font>
<font color=#0000ff>while</font>(sorter.hasMoreElements()) {
Object t = sorter.nextElement();
<font color=#009900>// RTTI to show class membership:</font>
<font color=#0000ff>if</font>(t <font color=#0000ff>instanceof</font> Aluminum)
alBin.addElement(t);
<font color=#0000ff>if</font>(t <font color=#0000ff>instanceof</font> Paper)
paperBin.addElement(t);
<font color=#0000ff>if</font>(t <font color=#0000ff>instanceof</font> Glass)
glassBin.addElement(t);
}
Trash.sumValue(alBin);
Trash.sumValue(paperBin);
Trash.sumValue(glassBin);
Trash.sumValue(bin);
}
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE><DIV ALIGN="LEFT"><P><A NAME="_Toc305593314"></A><A NAME="_Toc305628786"></A><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The first thing you’ll notice
is the <A NAME="Index2930"></A><B>package</B> statement:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#0000ff>package</font> c16.recyclea;</PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">This means that in the source code
listings available for the book, this file will be placed in the subdirectory
<B>recyclea</B> that branches off from the subdirectory <B>c16</B> (for Chapter
16). The unpacking tool in Chapter 17 takes care of placing it into the correct
subdirectory. The reason for doing this is that this chapter rewrites this
particular example a number of times and by putting each version in its own
<B>package</B> the class names will not clash.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Several
<A NAME="Index2931"></A><B>Vector</B> objects are created to hold <B>Trash</B>
handles. Of course, <B>Vector</B>s<B> </B>actually hold <B>Object</B>s so
they’ll hold anything at all. The reason they hold <B>Trash </B>(or
something derived from <B>Trash</B>) is only because you’ve been careful
to not put in anything except <B>Trash</B>. If you do put something
“wrong” into the <B>Vector</B>, you won’t get any compile-time
warnings or errors – you’ll find out only via an exception at
run-time.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">When the <B>Trash</B> handles are
added, they lose their specific identities and become simply <B>Object</B>
handles (they are <A NAME="Index2932"></A><I>upcast</I>). However, because of
polymorphism <A NAME="Index2933"></A>the proper behavior still occurs when the
dynamically-bound methods<A NAME="Index2934"></A> are called through the
<A NAME="Index2935"></A><B>Enumeration</B> <B>sorter</B>, once the resulting
<A NAME="Index2936"></A><B>Object</B> has been cast back to <B>Trash</B>.
<B>sumValue( )</B> also uses an <B>Enumeration </B>to perform operations on
every object in the <B>Vector</B>. </FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">It looks silly to upcast the types
of <B>Trash</B> into a collection holding base type handles, and then turn
around and downcast. Why not just put the trash into the appropriate receptacle
in the first place? (Indeed, this is the whole enigma of recycling). In this
program it would be easy to repair, but sometimes a system’s structure and
flexibility can benefit greatly from downcasting.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The program satisfies the design
requirements: it works. This might be fine as long as it’s a one-shot
solution. However, a useful program tends to evolve over time, so you must ask,
“What if the situation changes?” For example, cardboard is now a
valuable recyclable commodity, so how will that be integrated into the system
(especially if the program is large and complicated). Since the above
<A NAME="Index2937"></A>type-check coding in the <B>switch</B> statement could
be scattered throughout the program, you must go find all that code every time a
new type is added, and if you miss one the compiler won’t give you any
help by pointing out an error.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The key to the
<A NAME="Index2938"></A>misuse of RTTI here is that <I>every type is tested</I>.
If you’re looking for only a subset of types because that subset needs
special treatment, that’s probably fine. But if you’re hunting for
every type inside a switch statement, then you’re probably missing an
important point, and definitely making your code less maintainable. In the next
section we’ll look at how this program evolved over several stages to
become much more flexible. This should prove a valuable example in program
design.</FONT><A NAME="_Toc375545414"></A><A NAME="_Toc408018799"></A><BR></P></DIV>
<A NAME="Heading556"></A><FONT FACE = "Verdana"><H2 ALIGN="LEFT">
Improving the design</H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The solutions in <I>Design
Patterns</I> are organized around the question “What will change as this
program evolves?” This is usually the most important question that you can
ask about any design. If you can build your system around the answer, the
results will be two-pronged: not only will your system allow easy (and
inexpensive) maintenance, but you might also produce components that are
reusable, so that other systems can be built more cheaply. This is the promise
of object-oriented programming, but it doesn’t happen automatically; it
requires thought and insight on your part. In this section we’ll see how
this process can happen during the refinement of a system.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The answer to the question
“What will change?” for the recycling system is a common one: more
types will be added to the system. The goal of the design, then, is to make this
addition of types as painless as possible. In the recycling program, we’d
like to encapsulate all places where specific type information is mentioned, so
(if for no other reason) any changes can be localized to those encapsulations.
It turns out that this process also cleans up the rest of the code
considerably.</FONT><A NAME="_Toc375545415"></A><A NAME="_Toc408018800"></A><BR></P></DIV>
<A NAME="Heading557"></A><FONT FACE = "Verdana"><H3 ALIGN="LEFT">
“Make more objects”</H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">This brings up a general
object-oriented design principle that I first heard spoken by
<A NAME="Index2939"></A>Grady Booch: “If the design is too complicated,
make more objects.” This is simultaneously counterintuitive and
ludicrously simple, and yet it’s the most useful guideline I’ve
found. (You might observe that “making more objects” is often
equivalent to “add another level of indirection.”) In general, if
you find a place with messy code, consider what sort of class would clean that
up. Often the side effect of cleaning up the code will be a system that has
better structure and is more flexible.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Consider first the place where
<B>Trash</B> objects are created, which is a <B>switch</B> statement inside
<B>main( )</B>:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE> <font color=#0000ff>for</font>(<font color=#0000ff>int</font> i = 0; i < 30; i++)
<font color=#0000ff>switch</font>((<font color=#0000ff>int</font>)(Math.random() * 3)) {
<font color=#0000ff>case</font> 0 :
bin.addElement(<font color=#0000ff>new</font>
Aluminum(Math.random() * 100));
<font color=#0000ff>break</font>;
<font color=#0000ff>case</font> 1 :
bin.addElement(<font color=#0000ff>new</font>
Paper(Math.random() * 100));
<font color=#0000ff>break</font>;
<font color=#0000ff>case</font> 2 :
bin.addElement(<font color=#0000ff>new</font>
Glass(Math.random() * 100));
}</PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">This is definitely messy, and also
a place where you must change code whenever a new type is added. If new types
are commonly added, a better solution is a single method that takes all of the
necessary information and produces a handle to an object of the correct type,
already upcast to a trash object. In <I>Design Patterns</I> this is broadly
referred to as a <A NAME="Index2940"></A><A NAME="Index2941"></A><I>creational
pattern</I> (of which there are several). The specific pattern that will be
applied here is a variant of the
<A NAME="Index2942"></A><A NAME="Index2943"></A><I>Factory Method</I>. Here, the
factory method is a <B>static</B> member of <B>Trash</B>, but more commonly it
is a method that is overridden in the derived class.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The idea of the factory method is
that you pass it the essential information it needs to know to create your
object, then stand back and wait for the handle (already upcast to the base
type) to pop out as the return value. From then on, you treat the object
polymorphically. Thus, you never even need to know the exact type of object
that’s created. In fact, the factory method hides it from you to prevent
accidental misuse. If you want to use the object without polymorphism, you must
explicitly use RTTI and casting.</FONT><BR></P></DIV>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -