📄 chapter16.html
字号:
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">But there’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? “Creating more objects” 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’s a new class called
<B>Info</B> that contains all of the necessary information for the
<B>factory</B> method to create the appropriate <B>Trash</B> object.
Here’s a simple implementation of <B>Info</B>:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#0000ff>class</font> Info {
<font color=#0000ff>int</font> type;
<font color=#009900>// Must change this to add another type:</font>
<font color=#0000ff>static</font> <font color=#0000ff>final</font> <font color=#0000ff>int</font> MAX_NUM = 4;
<font color=#0000ff>double</font> data;
Info(<font color=#0000ff>int</font> typeNum, <font color=#0000ff>double</font> dat) {
type = typeNum % MAX_NUM;
data = dat;
}
}</PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">An <B>Info</B> object’s only
job is to hold information for the <B>factory( )</B> method. Now, if
there’s a situation in which <B>factory( )</B> needs more or
different information to create a new type of <B>Trash</B> object, the
<B>factory( )</B> interface doesn’t need to be changed. The
<B>Info</B> class can be changed by adding new data and new constructors, or in
the more typical object-oriented fashion of subclassing.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The <B>factory( )</B> method
for this simple example looks like this:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE> <font color=#0000ff>static</font> Trash factory(Info i) {
<font color=#0000ff>switch</font>(i.type) {
<font color=#0000ff>default</font>: <font color=#009900>// To quiet the compiler</font>
<font color=#0000ff>case</font> 0:
<font color=#0000ff>return</font> <font color=#0000ff>new</font> Aluminum(i.data);
<font color=#0000ff>case</font> 1:
<font color=#0000ff>return</font> <font color=#0000ff>new</font> Paper(i.data);
<font color=#0000ff>case</font> 2:
<font color=#0000ff>return</font> <font color=#0000ff>new</font> Glass(i.data);
<font color=#009900>// Two lines here:</font>
<font color=#0000ff>case</font> 3:
<font color=#0000ff>return</font> <font color=#0000ff>new</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( )</B> uses an elaborate algorithm. The point is that
it’s now hidden away in one place, and you know to come to this place when
you add new types.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The creation of new objects is now
much simpler in <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++)
bin.addElement(
Trash.factory(
<font color=#0000ff>new</font> Info(
(<font color=#0000ff>int</font>)(Math.random() * Info.MAX_NUM),
Math.random() * 100)));</PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">An <B>Info</B> object is created to
pass the data into <B>factory( )</B>, which in turn produces some kind of
<B>Trash</B> object on the heap and returns the handle that’s added to the
<B>Vector</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>Info</B> object is automated. For example,
a <B>Vector</B> of arguments can be passed into the constructor of an
<B>Info</B> object (or directly into a <B>factory( )</B> call, for that
matter). This requires that the arguments be parsed and checked at runtime, but
it does provide the greatest flexibility.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">You can see from this code what
“<A NAME="Index2944"></A><A NAME="Index2945"></A><A NAME="Index2946"></A>vector
of change” 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.</FONT><A NAME="_Toc375545418"></A><A NAME="_Toc408018801"></A><BR></P></DIV>
<A NAME="Heading558"></A><FONT FACE = "Verdana"><H3 ALIGN="LEFT">
A pattern for prototyping creation</H3></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( )</B> method. If new types are
regularly being added to the system, the <B>factory( )</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 – including its creation – 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.</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
“<A NAME="Index2947"></A><A NAME="Index2948"></A>prototype” 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’re interested
in making. The objects in this sequence are used <I>only</I> for making new
objects, using an operation that’s not unlike the
<A NAME="Index2949"></A><B>clone( )</B> scheme built into Java’s root
class <B>Object</B>. In this case, we’ll name the cloning method
<B>tClone( )</B>.<B> </B>When you’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.</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( )</B> method
doesn’t need to be changed when a new type is added to the system.
</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’s already support for creating new
objects if you have a handle to the <B>Class</B> object. With
<A NAME="Index2950"></A><A NAME="Index2951"></A>Java 1.1 <I>reflection</I>
(introduced in Chapter 11) you can call a constructor even if you have only a
handle to the <B>Class</B> object. This is the perfect solution for the
prototyping problem.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The list of prototypes will be
represented indirectly by a list of handles to all the <B>Class</B> objects you
want to create. In addition, if the prototyping fails, the
<B>factory( )</B> method will assume that it’s because a particular
<B>Class</B> object wasn’t in the list, and it will attempt to load it. By
loading the prototypes dynamically like this, the <B>Trash</B> class
doesn’t need to know what types it is working with, so it doesn’t
need any modifications when you add new types. This allows it to be easily
reused throughout the rest of the chapter.</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: Trash.java</font>
<font color=#009900>// Base class for Trash recycling examples</font>
<font color=#0000ff>package</font> c16.trash;
<font color=#0000ff>import</font> java.util.*;
<font color=#0000ff>import</font> java.lang.reflect.*;
<font color=#0000ff>public</font> <font color=#0000ff>abstract</font> <font color=#0000ff>class</font> Trash {
<font color=#0000ff>private</font> <font color=#0000ff>double</font> weight;
Trash(<font color=#0000ff>double</font> wt) { weight = wt; }
Trash() {}
<font color=#0000ff>public</font> <font color=#0000ff>abstract</font> <font color=#0000ff>double</font> value();
<font color=#0000ff>public</font> <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>public</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();
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=#009900>// Remainder of class provides support for</font>
<font color=#009900>// prototyping:</font>
<font color=#0000ff>public</font> <font color=#0000ff>static</font> <font color=#0000ff>class</font> PrototypeNotFoundException
<font color=#0000ff>extends</font> Exception {}
<font color=#0000ff>public</font> <font color=#0000ff>static</font> <font color=#0000ff>class</font> CannotCreateTrashException
<font color=#0000ff>extends</font> Exception {}
<font color=#0000ff>private</font> <font color=#0000ff>static</font> Vector trashTypes =
<font color=#0000ff>new</font> Vector();
<font color=#0000ff>public</font> <font color=#0000ff>static</font> Trash factory(Info info)
<font color=#0000ff>throws</font> PrototypeNotFoundException,
CannotCreateTrashException {
<font color=#0000ff>for</font>(<font color=#0000ff>int</font> i = 0; i < trashTypes.size(); i++) {
<font color=#009900>// Somehow determine the new type</font>
<font color=#009900>// to create, and create one:</font>
Class tc =
(Class)trashTypes.elementAt(i);
<font color=#0000ff>if</font> (tc.getName().indexOf(info.id) != -1) {
<font color=#0000ff>try</font> {
<font color=#009900>// Get the dynamic constructor method</font>
<font color=#009900>// that takes a double argument:</font>
Constructor ctor =
tc.getConstructor(
<font color=#0000ff>new</font> Class[] {<font color=#0000ff>double</font>.<font color=#0000ff>class</font>});
<font color=#009900>// Call the constructor to create a </font>
<font color=#009900>// new object:</font>
<font color=#0000ff>return</font> (Trash)ctor.newInstance(
<font color=#0000ff>new</font> Object[]{<font color=#0000ff>new</font> Double(info.data)});
} <font color=#0000ff>catch</font>(Exception ex) {
ex.printStackTrace();
<font color=#0000ff>throw</font> <font color=#0000ff>new</font> CannotCreateTrashException();
}
}
}
<font color=#009900>// Class was not in the list. Try to load it,</font>
<font color=#009900>// but it must be in your class path!</font>
<font color=#0000ff>try</font> {
System.out.println(<font color=#004488>"Loading "</font> + info.id);
trashTypes.addElement(
Class.forName(info.id));
} <font color=#0000ff>catch</font>(Exception e) {
e.printStackTrace();
<font color=#0000ff>throw</font> <font color=#0000ff>new</font> PrototypeNotFoundException();
}
<font color=#009900>// Loaded successfully. Recursive call </font>
<font color=#009900>// should work this time:</font>
<font color=#0000ff>return</font> factory(info);
}
<font color=#0000ff>public</font> <font color=#0000ff>static</font> <font color=#0000ff>class</font> Info {
<font color=#0000ff>public</font> String id;
<font color=#0000ff>public</font> <font color=#0000ff>double</font> data;
<font color=#0000ff>public</font> Info(String name, <font color=#0000ff>double</font> data) {
id = name;
<font color=#0000ff>this</font>.data = data;
}
}
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The basic <B>Trash</B> class and
<B>sumValue( )</B> remain as before. The rest of the class supports the
prototyping pattern. You first see two
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -