友情提示:如果本网页打开太慢或显示不完整,请尝试鼠标右键“刷新”本网页!
Java编程思想第4版[中文版](PDF格式)-第141部分
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部! 如果本书没有阅读完,想下次继续接着阅读,可使用上方 "收藏到我的浏览器" 功能 和 "加入书签" 功能!
Double,但在调用的时候必须用Double 传递。幸运的是,这个问题只存在于基本数据类型中间。
理解了具体的过程后,再来创建一个新对象,并且只为它提供一个Class 句柄,事情就变得非常简单了。就
目前的情况来说,内部循环中的return 永远不会执行,我们在终点就会退出。在这儿,程序动态装载 Class
对象,并把它加入 trashTypes (垃圾类型)列表,从而试图纠正这个问题。若仍然找不到真正有问题的地
方,同时装载又是成功的,那么就重复调用factory 方法,重新试一遍。
正如大家会看到的那样,这种设计方案最大的优点就是不需要改动代码。无论在什么情况下,它都能正常地
使用(假定所有Trash 子类都包含了一个构建器,用以获取单个 double 参数)。
1。 Trash 子类
为了与原型机制相适应,对Trash 每个新子类唯一的要求就是在其中包含了一个构建器,指示它获取一个
double 参数。Java 1。1 的“反射”机制可负责剩下的所有工作。
下面是不同类型的Trash,每种类型都有它们自己的文件里,但都属于 Trash 包的一部分(同样地,为了方
便在本章内重复使用):
//: Aluminum。java
// The Aluminum class with prototyping
package c16。trash;
public class Aluminum extends Trash {
private static double val = 1。67f;
599
…………………………………………………………Page 601……………………………………………………………
public Aluminum(double wt) { super(wt); }
public double value() { return val; }
public static void value(double newVal) {
val = newVal;
}
} ///:~
//: Paper。java
// The Paper class with prototyping
package c16。trash;
public class Paper extends Trash {
private static double val = 0。10f;
public Paper(double wt) { super(wt); }
public double value() { return val; }
public static void value(double newVal) {
val = newVal;
}
} ///:~
//: Glass。java
// The Glass class with prototyping
package c16。trash;
public class Glass extends Trash {
private static double val = 0。23f;
public Glass(double wt) { super(wt); }
public double value() { return val; }
public static void value(double newVal) {
val = newVal;
}
} ///:~
下面是一种新的Trash 类型:
//: Cardboard。java
// The Cardboard class with prototyping
package c16。trash;
public class Cardboard extends Trash {
private static double val = 0。23f;
public Cardboard(double wt) { super(wt); }
public double value() { return val; }
public static void value(double newVal) {
val = newVal;
}
} ///:~
可以看出,除构建器以外,这些类根本没有什么特别的地方。
2。 从外部文件中解析出Trash
与Trash 对象有关的信息将从一个外部文件中读取。针对Trash 的每个方面,文件内列出了所有必要的信
息——每行都代表一个方面,采用“垃圾(废品)名称:值”的固定格式。例如:
c16。Trash。Glass:54
600
…………………………………………………………Page 602……………………………………………………………
c16。Trash。Paper:22
c16。Trash。Paper:11
c16。Trash。Glass:17
c16。Trash。Aluminum:89
c16。Trash。Paper:88
c16。Trash。Aluminum:76
c16。Trash。Cardboard:96
c16。Trash。Aluminum:25
c16。Trash。Aluminum:34
c16。Trash。Glass:11
c16。Trash。Glass:68
c16。Trash。Glass:43
c16。Trash。Aluminum:27
c16。Trash。Cardboard:44
c16。Trash。Aluminum:18
c16。Trash。Paper:91
c16。Trash。Glass:63
c16。Trash。Glass:50
c16。Trash。Glass:80
c16。Trash。Aluminum:81
c16。Trash。Cardboard:12
c16。Trash。Glass:12
c16。Trash。Glass:54
c16。Trash。Aluminum:36
c16。Trash。Aluminum:93
c16。Trash。Glass:93
c16。Trash。Paper:80
c16。Trash。Glass:36
c16。Trash。Glass:12
c16。Trash。Glass:60
c16。Trash。Paper:66
c16。Trash。Aluminum:36
c16。Trash。Cardboard:22
注意在给定类名的时候,类路径必须包含在内,否则就找不到类。
为解析它,每一行内容都会读入,并用字串方法 indexOf()来建立“:”的一个索引。首先用字串方法
substring()取出垃圾的类型名称,接着用一个静态方法 Double。valueOf()取得相应的值,并转换成一个
double值。trim()方法则用于删除字串两头的多余空格。
Trash 解析器置入单独的文件中,因为本章将不断地用到它。如下所示:
//: ParseTrash。java
// Open a file and parse its contents into
// Trash objects; placing each into a Vector
package c16。trash;
import java。util。*;
import java。io。*;
public class ParseTrash {
public static void
fillBin(String filename; Fillable bin) {
try {
BufferedReader data =
new BufferedReader(
601
…………………………………………………………Page 603……………………………………………………………
new FileReader(filename));
String buf;
while((buf = data。readLine())!= null) {
String type = buf。substring(0;
buf。indexOf(':'))。trim();
double weight = Double。valueOf(
buf。substring(buf。indexOf(':') + 1)
。trim())。doubleValue();
bin。addTrash(
Trash。factory(
new Trash。Info(type; weight)));
}
data。close();
} catch(IOException e) {
e。printStackTrace();
} catch(Exception e) {
e。printStackTrace();
}
}
// Special case to handle Vector:
public static void
fillBin(String filename; Vector bin) {
fillBin(filename; new FillableVector(bin));
}
} ///:~
在RecycleA。java 中,我们用一个Vector 容纳Trash 对象。然而,亦可考虑采用其他集合类型。为做到这一
点,fillBin()的第一个版本将获取指向一个 Fillable 的句柄。后者是一个接口,用于支持一个名为
addTrash()的方法:
//: Fillable。java
// Any object that can be filled with Trash
package c16。trash;
public interface Fillable {
void addTrash(Trash t);
} ///:~
支持该接口的所有东西都能伴随fillBin 使用。当然,Vector 并未实现Fillable ,所以它不能工作。由于
Vector 将在大多数例子中应用,所以最好的做法是添加另一个过载的 fillBin()方法,令其以一个 Vector 作
为参数。利用一个适配器(Adapter)类,这个Vector 可作为一个 Fillable 对象使用:
//: FillableVector。java
// Adapter that makes a Vector Fillable
package c16。trash;
import java。util。*;
public class FillableVector implements Fillable {
private Vector v;
public FillableVector(Vector vv) { v = vv; }
public void addTrash(Trash t) {
v。addElement(t);
}
602
…………………………………………………………Page 604……………………………………………………………
} ///:~
可以看到,这个类唯一的任务就是负责将 Fillable 的addTrash()同Vector 的addElement()方法连接起来。
利用这个类,已过载的 fillBin()方法可在ParseTrash。java 中伴随一个Vector 使用:
public static void
fillBin(String filename; Vector bin) {
fillBin(filename; new FillableVector(bin));
}
这种方案适用于任何频繁用到的集合类。除此以外,集合类还可提供它自己的适配器类,并实现 Fillable
(稍后即可看到,在DynaTrash。java 中)。
3。 原型机制的重复应用
现在,大家可以看到采用原型技术的、修订过的RecycleA。java 版本了:
//: RecycleAP。java
// Recycling with RTTI and Prototypes
package c16。recycleap;
import c16。trash。*;
import java。util。*;
public class RecycleAP {
public static void main(String'' args) {
Vector bin = new Vector();
// Fill up the Trash bin:
ParseTrash。fillBin(〃Trash。dat〃; bin);
Vector
glassBin = new Vector();
paperBin = new Vector();
alBin = new Vector();
Enumeration sorter = bin。elements();
// Sort the Trash:
while(sorter。hasMoreElements()) {
Object t = sorter。nextElement();
// RTTI to show class membership:
if(t instanceof Aluminum)
alBin。addElement(t);
if(t instanceof Paper)
paperBin。addElement(t);
if(t instanceof Glass)
glassBin。addElement(t);
}
Trash。sumValue(alBin);
Trash。sumValue(paperBin);
Trash。sumValue(glassBin);
Trash。sumValue(bin);
}
} ///:~
所有Trash 对象——以及ParseTrash 及支撑类——现在都成为名为c16。trash 的一个包的一部分,所以它们
可以简单地导入。
无论打开包含了 Trash 描述信息的数据文件,还是对那个文件进行解析,所有涉及到的操作均已封装到
603
…………………………………………………………Page 605……………………………………………………………
static (静态)方法ParseTrash。fillBin()里。所以它现在已经不是我们设计过程中要注意的一个重点。在
本章剩余的部分,大家经常都会看到无论添加的是什么类型的新类,ParseTrash。fillBin()都会持续工作,
不会发生改变,这无疑是一种优良的设计方案。
提到对象的创建,这一方案确实已将新类型加入系统所需的变动严格地“本地化”了。但在使用 RTTI 的过程
中,却存在着一个严重的问题,这里已明确地显露出来。程序表面上工作得很好,但却永远侦测到不能“硬
纸板”(Cardboard)这种新的废品类型——即使列表里确实有一个硬纸板类型!之所以会出现这种情况,完
全是由于使用了RTTI 的缘故。RTTI 只会查找那些我们告诉它查找的东西。RTTI 在这里错误的用法是“系统
中的每种类型”都进行了测试,而不是仅测试一种类型或者一个类型子集。正如大家以后会看到的那样,在
测试每一种类型时可换用其他方式来运用多形性特征。但假如以这种形式过多地使用 RTTI,而且又在自己的
系统里添加了一种新类型,很容易就会忘记在程序里作出适当的改动,从而埋下以后难以发现的 Bug。因
此,在这种情况下避免使用RTTI 是很有必要的,这并不仅仅是为了表面好看——也是为了产生更易维护的代
码。
16。5 抽象的应用
走到这一步,接下来该考虑一下设计方案剩下的部分了——在哪里使用类?既然归类到垃圾箱的办法非常不
雅且过于暴露,为什么不隔离那个过程,把它隐藏到一个类里呢?这就是著名的“如果必须做不雅的事情,
至少应将其本地化到一个类里”规则。看起来就象下面这样:
现在,只要一种新类型的Trash 加入方法,对TrashSorter 对象的初始化就必须变动。可以想象,
TrashSorter 类看起来应该象下面这个样子:
class TrashSorter extends Vector {
void sort(Trash t) { /* 。。。 */ }
}
也就是说,TrashSorter 是由一系列句柄构成的 Vector (系列),而那些句柄指向的又是由Trash 句柄构成
的Vector;利用 addElement(),可以安装新的TrashSorter,如下所示:
TrashSorter ts = new TrashSorter();
ts。addElement(new Vector());
但是现在,sort()却成为一个问题。用静态方式编码的方法如何应付一种新类型加入的事实呢?为解决这个
问题,必须从sort()里将类型信息删除,使其需要做的所有事情就是调用一个通用方法,用它照料涉及类型
处理的所有细节。这当然是对一个动态绑定方法进行描述的另一种方式。所以sort()会在序列中简单地遍
历,并为每个Vector 都调用一个动态绑定方法。由于这个方法的任务是收集它感兴趣的垃圾片,所以称之为
grab(Trash)。结构现在变成了下面这样:
604
…………………………………………………………Page 606……………………………………………………………
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部!
温馨提示: 温看小说的同时发表评论,说出自己的看法和其它小伙伴们分享也不错哦!发表书评还可以获得积分和经验奖励,认真写原创书评 被采纳为精评可以获得大量金币、积分和经验奖励哦!