⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 sect14.htm

📁 this is the most basic to learn python
💻 HTM
📖 第 1 页 / 共 5 页
字号:
<B>sumValue(&#160;)</B> also takes an <B>Iterator </B>to perform operations on
every object in the <B>ArrayList</B>.
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_343">Add Comment</A></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">It looks silly to upcast the types of
<B>Trash</B> into a container holding base type references, 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&#146;s structure and
flexibility can benefit greatly from downcasting.
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_344">Add Comment</A></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&#146;s a one-shot
solution. However, a useful program tends to evolve over time, so you must ask,
&#147;What if the situation changes?&#148; 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="Index39"></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&#146;t give you any
help by pointing out an error.
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_345">Add Comment</A></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The key to the
<A NAME="Index40"></A>misuse of RTTI here is that <I>every type is tested</I>.
If you&#146;re looking for only a subset of types because that subset needs
special treatment, that&#146;s probably fine. But if you&#146;re hunting for
every type inside a switch statement, then you&#146;re probably missing an
important point, and definitely making your code less maintainable. In the next
section we&#146;ll look at how this program evolved over several stages to
become much more flexible. This should prove a valuable example in program
design.
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_346">Add Comment</A></FONT><A NAME="_Toc375545414"></A><A NAME="_Toc476705920"></A><A NAME="_Toc534420133"></A><BR></P></DIV>
<A NAME="Heading84"></A><FONT FACE = "Verdana, Tahoma, Arial, Helvetica, Sans"><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 &#147;What will change as this program
evolves?&#148; 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&#146;t happen automatically; it requires thought and
insight on your part. In this section we&#146;ll see how this process can
happen during the refinement of a system.
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_347">Add Comment</A></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The answer to the question &#147;What
will change?&#148; 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&#146;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.
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_348">Add Comment</A></FONT><A NAME="_Toc375545415"></A><A NAME="_Toc476705921"></A><A NAME="_Toc534420134"></A><BR></P></DIV>
<A NAME="Heading85"></A><FONT FACE = "Verdana, Tahoma, Arial, Helvetica, Sans"><H3 ALIGN="LEFT">
&#147;Make more objects&#148;</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="Index41"></A>Grady Booch:
&#147;If the design is too complicated, make more objects.&#148; This is
simultaneously counterintuitive and ludicrously simple, and yet it&#146;s the
most useful guideline I&#146;ve found. (You might observe that &#147;making
more objects&#148; is often equivalent to &#147;add another level of
indirection.&#148;) 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.
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_349">Add Comment</A></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(&#160;)</B>:
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_350">Add Comment</A></FONT><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE>    <font color=#0000ff>for</font>(int i = 0 i &lt; 30 i++)
      switch((int)(Math.random() * 3)):
        case 0 :
          bin.add(new
            Aluminum(Math.random() * 100))
          <font color=#0000ff>break</font>
        case 1 :
          bin.add(new
            Paper(Math.random() * 100))
          <font color=#0000ff>break</font>
        case 2 :
          bin.add(new
            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 reference 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="Index42"></A><A NAME="Index43"></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="Index44"></A><A NAME="Index45"></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.
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_351">Add Comment</A></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 reference (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&#146;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.
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_352">Add Comment</A></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">But there&#146;s a little problem,
especially when you use the more complicated approach (not shown here) of making
the factory method in the base class and overriding it in the derived classes.
What if the information required in the derived class requires more or different
arguments? &#147;Creating more objects&#148; solves this problem. To implement
the factory method, the <B>Trash</B> class gets a new method called
<B>factory</B>. To hide the creational data, there&#146;s a new class called
<B>Messenger</B> that carries all of the necessary information for the
<B>factory</B> method to create the appropriate <B>Trash</B> object (we&#146;ve
started referring to <I>Messenger</I> as a design pattern, but it&#146;s simple
enough that you may not choose to elevate it to that status). Here&#146;s a
simple implementation of <B>Messenger</B>:
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_353">Add Comment</A></FONT><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#0000ff>class</font> Messenger:
  int type
  # Must change this to add another type:
  static final int MAX_NUM = 4
  double data
  <font color=#0000ff>def</font> __init__(self, int typeNum, double val):
    type = typeNum % MAX_NUM
    data = val
</PRE></FONT></BLOCKQUOTE><DIV ALIGN="LEFT"><P><FONT FACE="Georgia">A <B>Messenger</B> object&#146;s
only job is to hold information for the <B>factory(&#160;)</B> method. Now, if
there&#146;s a situation in which <B>factory(&#160;)</B> needs more or
different information to create a new type of <B>Trash</B> object, the
<B>factory(&#160;)</B> interface doesn&#146;t need to be changed. The
<B>Messenger</B> class can be changed by adding new data and new constructors,
or in the more typical object-oriented fashion of subclassing.
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_354">Add Comment</A></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The <B>factory(&#160;)</B> method for
this simple example looks like this:</FONT><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE>  static Trash factory(Messenger i):
    switch(i.type):
      default: # To quiet the compiler
      case 0:
        <font color=#0000ff>return</font> Aluminum(i.data)
      case 1:
        <font color=#0000ff>return</font> Paper(i.data)
      case 2:
        <font color=#0000ff>return</font> Glass(i.data)
      # Two lines here:
      case 3: 
        <font color=#0000ff>return</font> Cardboard(i.data)
</PRE></FONT></BLOCKQUOTE><DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Here, the determination of the
exact type of object is simple, but you can imagine a more complicated system in
which <B>factory(&#160;)</B> uses an elaborate algorithm. The point is that
it&#146;s now hidden away in one place, and you know to come to this place when
you add new types.
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_355">Add Comment</A></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The creation of new objects is now much
simpler in <B>main(&#160;)</B>:</FONT><BR></P></DIV>

<BLOCKQUOTE><FONT SIZE = "+1"><PRE>    <font color=#0000ff>for</font>(int i = 0 i &lt; 30 i++)
      bin.add(
        Trash.factory(
          Messenger(
            (int)(Math.random() * Messenger.MAX_NUM),
            Math.random() * 100)))</PRE></FONT></BLOCKQUOTE><DIV ALIGN="LEFT"><P><FONT FACE="Georgia">A
<B>Messenger</B> object is created to pass the data into <B>factory(&#160;)</B>,
which in turn produces some kind of <B>Trash</B> object on the heap and returns
the reference that&#146;s added to the <B>ArrayList</B> <B>bin</B>. Of course,
if you change the quantity and type of argument, this statement will still need
to be modified, but that can be eliminated if the creation of the
<B>Messenger</B> object is automated. For example, an <B>ArrayList</B> of
arguments can be passed into the constructor of a <B>Messenger</B> object (or
directly into a <B>factory(&#160;)</B> call, for that matter). This requires
that the arguments be parsed and checked at run time, but it does provide the
greatest flexibility.
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_356">Add Comment</A></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">You can see from this code what
&#147;<A NAME="Index46"></A><A NAME="Index47"></A><A NAME="Index48"></A>vector
of change&#148; problem the factory is responsible for solving: if you add new
types to the system (the change), the only code that must be modified is within
the factory, so the factory isolates the effect of that change.
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_357">Add Comment</A></FONT><A NAME="_Toc375545418"></A><A NAME="_Toc476705922"></A><A NAME="_Toc534420135"></A><BR></P></DIV>
<A NAME="Heading86"></A><FONT FACE = "Verdana, Tahoma, Arial, Helvetica, Sans"><H2 ALIGN="LEFT">
A pattern for prototyping creation</H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">A problem with the design above is that
it still requires a central location where all the types of the objects must be
known: inside the <B>factory(&#160;)</B> method. If new types are regularly
being added to the system, the <B>factory(&#160;)</B> method must be changed for
each new type. When you discover something like this, it is useful to try to go
one step further and move <I>all</I> of the information about the
type&#151;including its creation&#151;into the class representing that type.
This way, the only thing you need to do to add a new type to the system is to
inherit a single class.
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_358">Add Comment</A></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">To move the information concerning type
creation into each specific type of <B>Trash</B>,<B> </B>the
&#147;<A NAME="Index49"></A><A NAME="Index50"></A>prototype&#148; pattern
(from the <I>Design Patterns </I>book) will be used. The general idea is that
you have a master sequence of objects, one of each type you&#146;re interested
in making. The objects in this sequence are used <I>only</I> for making new
objects, using an operation that&#146;s not unlike the
<A NAME="Index51"></A><B>clone(&#160;)</B> scheme built into Java&#146;s root
class <B>Object</B>. In this case, we&#146;ll name the cloning method
<B>tClone(&#160;)</B>.<B> </B>When you&#146;re ready to make a new object,
presumably you have some sort of information that establishes the type of object
you want to create, then you move through the master sequence comparing your
information with whatever appropriate information is in the prototype objects in
the master sequence. When you find one that matches your needs, you clone it.
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_359">Add Comment</A></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">In this scheme there is no hard-coded
information for creation. Each object knows how to expose appropriate
information and how to clone itself. Thus, the <B>factory(&#160;)</B> method
doesn&#146;t need to be changed when a new type is added to the system.
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_360">Add Comment</A></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">One approach to the problem of
prototyping is to add a number of methods to support the creation of new
objects. However, in Java 1.1 there&#146;s already support for creating new
objects if you have a reference to the <B>Class</B> object. With
<A NAME="Index52"></A><A NAME="Index53"></A>Java 1.1 <I>reflection</I>
(introduced in Chapter 12 of <I>Thinking in Java, 2<SUP>nd</SUP> edition</I>)
you can call a constructor even if you have only a reference to the <B>Class</B>
object. This is the perfect solution for the prototyping problem.
<A HREF="http://www.mindview.net/Books/TIPython/BackTalk/FindPage/A_361">Add Comment</A></FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The list of prototypes will be
represented indirectly by a list of references to all the <B>Class</B> objects

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -