📄 sect14.htm
字号:
<BLOCKQUOTE><FONT SIZE = "+1"><PRE># c12:trash:Trash.dat
c12.trash.Glass:54
c12.trash.Paper:22
c12.trash.Paper:11
c12.trash.Glass:17
c12.trash.Aluminum:89
c12.trash.Paper:88
c12.trash.Aluminum:76
c12.trash.Cardboard:96
c12.trash.Aluminum:25
c12.trash.Aluminum:34
c12.trash.Glass:11
c12.trash.Glass:68
c12.trash.Glass:43
c12.trash.Aluminum:27
c12.trash.Cardboard:44
c12.trash.Aluminum:18
c12.trash.Paper:91
c12.trash.Glass:63
c12.trash.Glass:50
c12.trash.Glass:80
c12.trash.Aluminum:81
c12.trash.Cardboard:12
c12.trash.Glass:12
c12.trash.Glass:54
c12.trash.Aluminum:36
c12.trash.Aluminum:93
c12.trash.Glass:93
c12.trash.Paper:80
c12.trash.Glass:36
c12.trash.Glass:12
c12.trash.Glass:60
c12.trash.Paper:66
c12.trash.Aluminum:36
c12.trash.Cardboard:22
# :~</PRE></FONT></BLOCKQUOTE><DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Note that the class path must
be included when giving the class names, otherwise the class will not be found.
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_377">Add Comment</A></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">This file is read using the
previously-defined <B>StringList </B>tool, and each line is picked aparat using
</FONT><FONT FACE="Georgia"> the <A NAME="Index68"></A><B>String </B>method
<A NAME="Index69"></A><B>indexOf( )</B> to produce the index of the
‘<B>:</B>’. This is first used with the <B>String </B>method
<A NAME="Index70"></A><A NAME="Index71"></A><B>substring( ) </B>to extract
the name of the trash type, and next to get the weight that is turned into a
<B>double </B>with the <B>static <A NAME="Index72"></A>Double.valueOf( )
</B>method. The <A NAME="Index73"></A><A NAME="Index74"></A><B>trim( )</B>
method removes white space at both ends of a string.
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_378">Add Comment</A></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The <B>Trash </B>parser is placed in a
separate file since it will be reused throughout this chapter:
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_379">Add Comment</A></FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE># c12:trash:ParseTrash.py
# Parse file contents into Trash objects,
# placing each into a Fillable holder.
<font color=#0000ff>class</font> ParseTrash:
<font color=#0000ff>def</font> fillBin(String filename, Fillable bin):
<font color=#0000ff>for</font> line <font color=#0000ff>in</font> open(filename).readlines():
String type = line.substring(0,
line.index(':')).strip()
double weight = Double.valueOf(
line.substring(line.index(':') + 1)
.strip()).doubleValue()
bin.addTrash(
Trash.factory(
Trash.Messenger(type, weight)))
# Special case to handle Collection:
<font color=#0000ff>def</font> fillBin(String filename, Collection bin):
fillBin(filename, FillableCollection(bin))
# :~</PRE></FONT></BLOCKQUOTE><DIV ALIGN="LEFT"><P><FONT FACE="Georgia">In <B>RecycleA.py</B>, an
<B>ArrayList</B> was used to hold the <B>Trash</B> objects. However, other types
of containers can be used as well. To allow for this, the first version of
<B>fillBin( )</B> takes a reference to a <B>Fillable</B>, which is simply
an <B>interface</B> that supports a method called <B>addTrash( )</B>:
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_380">Add Comment</A></FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE># c12:trash:Fillable.py
# Any object that can be filled with Trash.
<font color=#0000ff>class</font> Fillable:
<font color=#0000ff>def</font> addTrash(self, Trash t)
# :~</PRE></FONT></BLOCKQUOTE><DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Anything that supports this
interface can be used with <B>fillBin</B>. Of course, <B>Collection</B>
doesn’t implement <B>Fillable</B>, so it won’t work. Since
<B>Collection</B> is used in most of the examples, it makes sense to add a
second overloaded <B>fillBin( )</B> method that takes a <B>Collection</B>.
Any <B>Collection</B> can then be used as a <B>Fillable</B> object using an
adapter class:
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_381">Add Comment</A></FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE># c12:trash:FillableCollection.py
# Adapter that makes a Collection Fillable.
<font color=#0000ff>class</font> FillableCollection(Fillable):
private Collection c
<font color=#0000ff>def</font> __init__(self, Collection cc):
c = cc
<font color=#0000ff>def</font> addTrash(self, Trash t):
c.add(t)
# :~</PRE></FONT></BLOCKQUOTE><DIV ALIGN="LEFT"><P><FONT FACE="Georgia">You can see that the only job
of this class is to connect <B>Fillable</B>’s <B>addTrash( )</B>
method to <B>Collection’s</B> <B>add( )</B>. With this class in hand,
the overloaded <B>fillBin( )</B> method can be used with a
<B>Collection</B> in <B>ParseTrash.py</B>:
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_382">Add Comment</A></FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE> public static void
fillBin(String filename, Collection bin):
fillBin(filename, FillableCollection(bin))
</PRE></FONT></BLOCKQUOTE><DIV ALIGN="LEFT"><P><FONT FACE="Georgia">This approach works for any
container class that’s used frequently. Alternatively, the container class
can provide its own adapter that implements <B>Fillable</B>. (You’ll see
this later, in <B>DynaTrash.py</B>.)
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_383">Add Comment</A></FONT><A NAME="_Toc534420138"></A><BR></P></DIV>
<A NAME="Heading89"></A><FONT FACE = "Verdana, Tahoma, Arial, Helvetica, Sans"><H3 ALIGN="LEFT">
Recycling with prototyping</H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Now you can see the revised version of
<B>RecycleA.py</B> using the
<A NAME="Index75"></A><A NAME="Index76"></A>prototyping technique:
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_384">Add Comment</A></FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE># c12:recycleap:RecycleAP.py
# Recycling with RTTI <font color=#0000ff>and</font> Prototypes.
<font color=#0000ff>class</font> RecycleAP(UnitTest):
Collection
bin = ArrayList(),
glassBin = ArrayList(),
paperBin = ArrayList(),
alBin = ArrayList()
<font color=#0000ff>def</font> __init__(self):
# Fill up the Trash bin:
ParseTrash.fillBin(
<font color=#004488>"..</font><font color=#004488>/trash</font><font color=#004488>/Trash.dat"</font>, bin)
<font color=#0000ff>def</font> test(self):
Iterator sorter = bin.iterator()
# Sort the Trash:
<font color=#0000ff>while</font>(sorter.hasNext()):
Object t = sorter.next()
# RTTI to show <font color=#0000ff>class</font> membership:
<font color=#0000ff>if</font>(t instanceof Aluminum)
alBin.add(t)
<font color=#0000ff>if</font>(t instanceof Paper)
paperBin.add(t)
<font color=#0000ff>if</font>(t instanceof Glass)
glassBin.add(t)
Trash.sumValue(alBin.iterator())
Trash.sumValue(paperBin.iterator())
Trash.sumValue(glassBin.iterator())
Trash.sumValue(bin.iterator())
<font color=#0000ff>def</font> main(self, String args[]):
RecycleAP().test()
# :~</PRE></FONT></BLOCKQUOTE><DIV ALIGN="LEFT"><P><FONT FACE="Georgia">All of the <B>Trash</B>
objects, as well as the <B>ParseTrash</B> and support classes, are now part of
the package <B>c12.trash</B>, so they are simply imported.
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_385">Add Comment</A></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The process of opening the data file
containing <B>Trash</B> descriptions and the parsing of that file have been
wrapped into the <B>static</B> method <B>ParseTrash.fillBin( )</B>, so now
it’s no longer a part of our design focus. You will see that throughout
the rest of the chapter, no matter what new classes are added,
<B>ParseTrash.fillBin( )</B> will continue to work without change, which
indicates a good design.
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_386">Add Comment</A></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">In terms of object creation, this design
does indeed severely localize the changes you need to make to add a new type to
the system. However, there’s a significant problem in the use of RTTI that
shows up clearly here. The program seems to run fine, and yet it never detects
any cardboard, even though there is cardboard in the list! This happens
<I>because</I> of the use of RTTI, which looks for only the types that you tell
it to look for. The clue that <A NAME="Index77"></A>RTTI is being misused is
that <I>every type in the system </I>is being tested, rather than a single type
or subset of types. As you will see later, there are ways to use polymorphism
instead when you’re testing for every type. But if you use RTTI a lot in
this fashion, and you add a new type to your system, you can easily forget to
make the necessary changes in your program and produce a difficult-to-find bug.
So it’s worth trying to eliminate RTTI in this case, not just for
aesthetic reasons—it produces more maintainable code.
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_387">Add Comment</A></FONT><A NAME="_Toc375545416"></A><A NAME="_Toc476705923"></A><A NAME="_Toc534420139"></A><BR></P></DIV>
<A NAME="Heading90"></A><FONT FACE = "Verdana, Tahoma, Arial, Helvetica, Sans"><H2 ALIGN="LEFT">
Abstracting usage</H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">With creation out of the way, it’s
time to tackle the remainder of the design: where the classes are used. Since
it’s the act of sorting into bins that’s particularly ugly and
exposed, why not take that process and hide it inside a class? This is the
principle of “If you must do something ugly, at least localize the
ugliness inside a class.” It looks like this:
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_388">Add Comment</A></FONT><BR></P></DIV>
<DIV ALIGN="CENTER"><FONT FACE="Georgia"><IMG SRC="TIPyth06.gif"></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The <B>TrashSorter</B> object
initialization must now be changed whenever a new type of <B>Trash</B> is added
to the model. You could imagine that the <B>TrashSorter</B> class might look
something like this:
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_389">Add Comment</A></FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#0000ff>class</font> TrashSorter(ArrayList):
<font color=#0000ff>def</font> sort(self, Trash t): <font color=#009900>/* ... */</font>
</PRE></FONT></BLOCKQUOTE><DIV ALIGN="LEFT"><P><FONT FACE="Georgia">That is, <B>TrashSorter</B> is an
<B>ArrayList</B> of references to <B>ArrayList</B>s of <B>Trash</B> references,
and with <B>add( )</B> you can install another one, like so:
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_390">Add Comment</A></FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE>TrashSorter ts = TrashSorter()
ts.add(ArrayList())</PRE></FONT></BLOCKQUOTE><DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Now, however,
<B>sort( )</B> becomes a problem. How does the statically-coded method deal
with the fact that a new type has been added? To solve this, the type
information must be removed from <B>sort( )</B> so that all it needs to do
is call a generic method that takes care of the details of type. This, of
course, is another way to describe a dynamically-bound method. So
<B>sort( )</B> will simply move through the sequence and call a
dynamically-bound method for each <B>ArrayList</B>. Since the job of this method
is to grab the pieces of trash it is interested in, it’s called
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -