📄 sect14.htm
字号:
<B>grab(Trash)</B>. The structure now looks like:
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_391">Add Comment</A></FONT><BR></P></DIV>
<DIV ALIGN="CENTER"><FONT FACE="Georgia"><IMG SRC="TIPyth07.gif"></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia"><B>TrashSorter</B> needs to call each
<B>grab( )</B> method and get a different result depending on what type of
<B>Trash</B> the current <B>ArrayList</B> is holding. That is, each
<B>ArrayList</B> must be aware of the type it holds. The classic approach to
this problem is to create a base “<B>Trash</B> bin” class and
inherit a new derived class for each different type you want to hold. If Java
had a parameterized type mechanism that would probably be the most
straightforward approach. But rather than hand-coding all the classes that such
a mechanism should be building for us, further observation can produce a better
approach.
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_392">Add Comment</A></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">A basic OOP design principle is
“Use data members for variation in state, use
<A NAME="Index78"></A>polymorphism for variation in behavior.” Your first
thought might be that the <B>grab( )</B> method certainly behaves
differently for an <B>ArrayList</B> that holds <B>Paper</B> than for one that
holds <B>Glass</B>. But what it does is strictly dependent on the type, and
nothing else. This could be interpreted as a different state, and since Java has
a class to represent type (<B>Class</B>) this can be used to determine the type
of <B>Trash</B> a particular <B>Tbin</B> will hold.
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_393">Add Comment</A></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The constructor for this <B>Tbin
</B>requires that you hand it the <B>Class</B> of your choice. This tells the
<B>ArrayList</B> what type it is supposed to hold. Then the <B>grab( )</B>
method uses <B>Class BinType</B> and RTTI to see if the <B>Trash</B> object
you’ve handed it matches the type it’s supposed to grab.
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_394">Add Comment</A></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Here is the new version of the
program:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE># c12:recycleb:RecycleB.py
# Containers that grab objects of interest.
# A container that admits only the right type
# of Trash (established <font color=#0000ff>in</font> the constructor):
<font color=#0000ff>class</font> Tbin:
private Collection list = ArrayList()
private Class type
<font color=#0000ff>def</font> __init__(self, Class binType): type = binType
<font color=#0000ff>def</font> grab(self, Trash t):
# Comparing <font color=#0000ff>class</font> types:
<font color=#0000ff>if</font>(t.getClass().equals(type)):
list.add(t)
<font color=#0000ff>return</font> 1 # Object grabbed
<font color=#0000ff>return</font> 0 # Object <font color=#0000ff>not</font> grabbed
<font color=#0000ff>def</font> iterator(self):
<font color=#0000ff>return</font> list.iterator()
<font color=#0000ff>class</font> TbinList(ArrayList):
<font color=#0000ff>def</font> sort(self, Trash t):
Iterator e = iterator() # Iterate over self
<font color=#0000ff>while</font>(e.hasNext())
<font color=#0000ff>if</font>(((Tbin)e.next()).grab(t)) <font color=#0000ff>return</font>
# Need a Tbin <font color=#0000ff>for</font> this type:
add(Tbin(t.getClass()))
sort(t) # Recursive call
<font color=#0000ff>class</font> RecycleB(UnitTest):
Collection bin = ArrayList()
TbinList trashBins = TbinList()
<font color=#0000ff>def</font> __init__(self):
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 it = bin.iterator()
<font color=#0000ff>while</font>(it.hasNext())
trashBins.sort((Trash)it.next())
Iterator e = trashBins.iterator()
<font color=#0000ff>while</font>(e.hasNext()):
Tbin b = (Tbin)e.next()
Trash.sumValue(b.iterator())
Trash.sumValue(bin.iterator())
<font color=#0000ff>def</font> main(self, String args[]):
RecycleB().test()
# :~</PRE></FONT></BLOCKQUOTE><DIV ALIGN="LEFT"><P><FONT FACE="Georgia"><B>Tbin</B> contains a
<B>Class</B> reference <B>type</B> which establishes in the constructor what
what type it should grab. The <B>grab()</B> method checks this type against the
object you pass it. Note that in this design, <B>grab()</B> only accepts
<B>Trash</B> objects so you get compile-time type checking on the base type, but
you could also just accept <B>Object</B> and it would still work.
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_395">Add Comment</A></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia"><B>T</B></FONT><FONT FACE="Georgia"><B>binList</B>
holds a set of <B>Tbin</B> references, so that <B>sort( )</B> can iterate
through the <B>Tbin</B>s when it’s looking for a match for the
<B>Trash</B> object you’ve handed it. If it doesn’t find a match, it
creates a new <B>Tbin</B> for the type that hasn’t been found, and makes a
recursive call to itself – the next time around, the new bin will be
found.
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_396">Add Comment</A></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Notice the genericity of this code: it
doesn’t change at all if new types are added. If the bulk of your code
doesn’t need changing when a new type is added (or some other change
occurs) then you have an easily extensible
system.</FONT><FONT FACE="Georgia"><A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_397">Add Comment</A></FONT><A NAME="_Toc375545417"></A><A NAME="_Toc476705924"></A><A NAME="_Toc534420140"></A><BR></P></DIV>
<A NAME="Heading91"></A><FONT FACE = "Verdana, Tahoma, Arial, Helvetica, Sans"><H2 ALIGN="LEFT">
Multiple dispatching</H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The above design is certainly
satisfactory. Adding new types to the system consists of adding or modifying
distinct classes without causing code changes to be propagated throughout the
system. In addition, RTTI is not “misused” as it was in
<B>RecycleA.py</B>. However, it’s possible to go one step further and take
a purist viewpoint about <A NAME="Index79"></A>RTTI and say that it should be
eliminated altogether from the operation of sorting the trash into bins.
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_398">Add Comment</A></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">To accomplish this, you must first take
the perspective that all type-dependent activities—such as detecting the
type of a piece of trash and putting it into the appropriate bin—should be
controlled through polymorphism and dynamic binding.
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_399">Add Comment</A></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The previous examples first sorted by
type, then acted on sequences of elements that were all of a particular type.
But whenever you find yourself picking out particular types, stop and think. The
whole idea of polymorphism (dynamically-bound method calls) is to handle
type-specific information for you. So why are you hunting for types?
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_400">Add Comment</A></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The answer is something you probably
don’t think about: Python performs only single dispatching. That is, if
you are performing an operation on more than one object whose type is unknown,
Python will invoke the dynamic binding mechanism on only one of those types.
This doesn’t solve the problem, so you end up detecting some types
manually and effectively producing your own dynamic binding behavior.
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_401">Add Comment</A></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The solution is called
<A NAME="Index80"></A><A NAME="Index81"></A><I>multiple dispatching</I>, which
means setting up a configuration such that a single method call produces more
than one dynamic method call and thus determines more than one type in the
process. To get this effect, you need to work with more than one type hierarchy:
you’ll need a type hierarchy for each dispatch. The following example
works with two hierarchies: the existing <B>Trash</B> family and a hierarchy of
the types of trash bins that the trash will be placed into. This second
hierarchy isn’t always obvious and in this case it needed to be created in
order to produce multiple dispatching (in this case there will be only two
dispatches, which is referred to as
<A NAME="Index82"></A><A NAME="Index83"></A><I>double dispatching</I>).
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_402">Add Comment</A></FONT><A NAME="_Toc476705925"></A><A NAME="_Toc534420141"></A><BR></P></DIV>
<A NAME="Heading92"></A><FONT FACE = "Verdana, Tahoma, Arial, Helvetica, Sans"><H3 ALIGN="LEFT">
Implementing the double dispatch</H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Remember that polymorphism can occur only
via method calls, so if you want double dispatching to occur, there must be two
method calls: one used to determine the type within each hierarchy. In the Trash
hierarchy there will be a new method called addToBin( ), which takes an
argument of an array of TypedBin. It uses this array to step through and try to
add itself to the appropriate bin, and this is where you'll see the double
dispatch. <IMG SRC="TIPyth08.gif"></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The new hierarchy is TypedBin, and it
contains its own method called add( ) that is also used polymorphically.
But here's an additional twist: add( ) is overloaded to take arguments of
the different types of trash. So an essential part of the double dispatching
scheme also involves overloading.Redesigning the program produces a dilemma:
it’s now necessary for the base class <B>Trash</B> to contain an
<B>addToBin( )</B> method. One approach is to copy all of the code and
change the base class. Another approach, which you can take when you don’t
have control of the source code, is to put the <B>addToBin( )</B> method
into an <B>interface</B>, leave <B>Trash</B> alone, and inherit new specific
types of <B>Aluminum</B>, <B>Paper</B>, <B>Glass</B>, and <B>Cardboard</B>. This
is the approach that will be taken here.
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_403">Add Comment</A></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Most of the classes in this design must
be <B>public</B>, so they are placed in their own files. Here’s the
interface:
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_404">Add Comment</A></FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE># c12:doubledispatch:TypedBinMember.py
# An <font color=#0000ff>class</font> <font color=#0000ff>for</font> adding the double
# dispatching method to the trash hierarchy
# without modifying the original hierarchy.
<font color=#0000ff>class</font> TypedBinMember:
# The method:
boolean addToBin(TypedBin[] tb)
# :~</PRE></FONT></BLOCKQUOTE><DIV ALIGN="LEFT"><P><FONT FACE="Georgia">In each particular subtype of
<B>Aluminum</B>, <B>Paper</B>, <B>Glass,</B> and <B>Cardboard</B>, the
<B>addToBin( )</B> method in the <B>interface TypedBinMember</B> is
implemented, but it <I>looks</I> like the code is exactly the same in each case:
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_405">Add Comment</A></FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE># c12:doubledispatch:DDAluminum.py
# Aluminum <font color=#0000ff>for</font> double dispatching.
<font color=#0000ff>class</font> DDAluminum(Aluminum)
implements TypedBinMember:
<font color=#0000ff>def</font> __init__(self, double wt): .__init__(wt)
<font color=#0000ff>def</font> addToBin(self, TypedBin[] tb):
<font color=#0000ff>for</font>(int i = 0 i < tb.length i++)
<font color=#0000ff>if</font>(tb[i].add(self))
<font color=#0000ff>return</font> 1
<font color=#0000ff>return</font> 0
# :~</PRE></FONT></BLOCKQUOTE>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE># c12:doubledispatch:DDPaper.py
# Paper <font color=#0000ff>for</font> double dispatching.
<font color=#0000ff>class</font> DDPaper(Paper)
implements TypedBinMember:
<font color=#0000ff>def</font> __init__(self, double wt): .__init__(wt)
<font color=#0000ff>def</font> addToBin(self, TypedBin[] tb):
<font color=#0000ff>for</font>(int i = 0 i < tb.length i++)
<font color=#0000ff>if</font>(tb[i].add(self))
<font color=#0000ff>return</font> 1
<font color=#0000ff>return</font> 0
# :~</PRE></FONT></BLOCKQUOTE>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE># c12:doubledispatch:DDGlass.py
# Glass <font color=#0000ff>for</font> double dispatching.
<font color=#0000ff>class</font> DDGlass(Glass)
implements TypedBinMember:
<font color=#0000ff>def</font> __init__(self, double wt): .__init__(wt)
<font color=#0000ff>def
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -