友情提示:如果本网页打开太慢或显示不完整,请尝试鼠标右键“刷新”本网页!
第三电子书 返回本书目录 加入书签 我的书架 我的书签 TXT全本下载 『收藏到我的浏览器』

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……………………………………………………………
返回目录 上一页 下一页 回到顶部 1 1
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部!
温馨提示: 温看小说的同时发表评论,说出自己的看法和其它小伙伴们分享也不错哦!发表书评还可以获得积分和经验奖励,认真写原创书评 被采纳为精评可以获得大量金币、积分和经验奖励哦!