📄 tij0177.html
字号:
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>factory( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
method. If new types are regularly being added to the system, the
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>factory( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
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
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><I>all</I></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
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><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">To
move the information concerning type creation into each specific type of
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Trash</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">,</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>
</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">the
“<A NAME="Index2959"></A><A NAME="Index2960"></A>prototype”
pattern (from the
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><I>Design
Patterns
</I></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">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
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><I>only</I></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
for making new objects, using an operation that’s not unlike the <A NAME="Index2961"></A></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>clone( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
scheme built into Java’s root class
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Object</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">.
In this case, we’ll name the cloning method
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>tClone( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">.</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>
</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">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><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">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
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>factory( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
method doesn’t need to be changed when a new type is added to the system.
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">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
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Class</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
object. With <A NAME="Index2962"></A><A NAME="Index2963"></A>Java
1.1
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><I>reflection</I></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
(introduced in Chapter 11) you can call a constructor even if you have only a
handle to the
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Class</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
object. This is the perfect solution for the prototyping problem.
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">The
list of prototypes will be represented indirectly by a list of handles to all
the
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Class</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
objects you want to create. In addition, if the prototyping fails, the
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>factory( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
method will assume that it’s because a particular
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Class</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
object wasn’t in the list, and it will attempt to load it. By loading the
prototypes dynamically like this, the
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Trash</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
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><P></DIV>
<font color="#990000"><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(
"weight of " +
<font color="#009900">// Using RTTI to get type</font>
<font color="#009900">// information about the class:</font>
t.getClass().getName() +
" = " + t.weight());
}
System.out.println("Total value = " + 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("Loading " + 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">///:~ </PRE></font></font><DIV ALIGN=LEFT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">The
basic
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Trash</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
class and
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>sumValue( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
remain as before. The rest of the class supports the prototyping pattern. You
first see two <A NAME="Index2964"></A><A NAME="Index2965"></A>inner
classes (which are made
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>static</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">,
so they are inner classes only for code organization purposes) describing
exceptions that can occur. This is followed by a
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Vector
trashTypes
</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">,
which is used to hold the
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Class</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
handles.
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">In
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Trash.factory( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">,
the
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>String</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
inside the
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Info
</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">object
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>id
</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">(a
different version of the
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Info</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
class than that of the prior discussion) contains the type name of the
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Trash
</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">to
be created; this
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>String</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
is compared to the
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Class</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
names in the list. If there’s a match, then that’s the object to
create. Of course, there are many ways to determine what object you want to
make. This one is used so that information read in from a file can be turned
into objects.
</FONT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">Once
you’ve discovered which kind of
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Trash</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
to create, then the <A NAME="Index2966"></A>reflection
methods come into play. The <A NAME="Index2967"></A></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>getConstructor( )</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
method takes an argument that’s an array of <A NAME="Index2968"></A></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Class</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
handles. This array represents the arguments, in their proper order, for the
constructor that you’re looking for. Here, the <A NAME="Index2969"></A><A NAME="Index2970"></A>array
is dynamically created using the Java 1.1<A NAME="Index2971"></A>
array-creation syntax:
</FONT><P></DIV><DIV ALIGN=LEFT><TT><FONT FACE="Courier New" SIZE=3 COLOR="Black">new
Class[] {double.class}
</FONT></TT><P></DIV><DIV ALIGN=LEFT><P></DIV><DIV ALIGN=LEFT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">This
code assumes that every
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>Trash</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
type has a constructor that takes a
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>double
</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">(and
notice that
</FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black"><B>double.class</B></FONT><FONT FACE="Carmina Md BT" SIZE=3 COLOR="Black">
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -