友情提示:如果本网页打开太慢或显示不完整,请尝试鼠标右键“刷新”本网页!阅读过程发现任何错误请告诉我们,谢谢!! 报告错误
一世书城 返回本书目录 我的书架 我的书签 TXT全本下载 进入书吧 加入书签

Java编程思想第4版[中文版](PDF格式)-第192章

按键盘上方向键 ← 或 → 可快速上下翻页,按键盘上的 Enter 键可回到本书目录页,按键盘上方向键 ↑ 可回到本页顶部!
————未阅读完?加入书签已便下次继续阅读!






16。4 改进设计  



 《Design Patterns》书内所有方案的组织都围绕“程序进化时会发生什么变化”这个问题展开。对于任何设 

计来说,这都可能是最重要的一个问题。若根据对这个问题的回答来构造自己的系统,就可以得到两个方面 

的结果:系统不仅更易维护(而且更廉价),而且能产生一些能够重复使用的对象,进而使其他相关系统的 

构造也变得更廉价。这正是面向对象程序设计的优势所在,但这一优势并不是自动体现出来的。它要求对我 

们对需要解决的问题有全面而且深入的理解。在这一节中,我们准备在系统的逐步改进过程中向大家展示如 

何做到这一点。  

就目前这个回收系统来说,对“什么会变化”这个问题的回答是非常普通的:更多的类型会加入系统。因 

此,设计的目标就是尽可能简化这种类型的添加。在回收程序中,我们准备把涉及特定类型信息的所有地方 

都封装起来。这样一来(如果没有别的原因),所有变化对那些封装来说都是在本地进行的。这种处理方式 

也使代码剩余的部分显得特别清爽。  



16。4。1  “制作更多的对象”  



这样便引出了面向对象程序设计时一条常规的准则,我最早是在Grady Booch 那里听说的:“若设计过于复 

杂,就制作更多的对象”。尽管听起来有些暧昧,且简单得可笑,但这确实是我知道的最有用一条准则(大 

家以后会注意到“制作更多的对象”经常等同于“添加另一个层次的迂回”)。一般情况下,如果发现一个 

地方充斥着大量繁复的代码,就需要考虑什么类能使它显得清爽一些。用这种方式整理系统,往往会得到一 

个更好的结构,也使程序更加灵活。  

首先考虑Trash 对象首次创建的地方,这是main()里的一个switch语句:  

  

    for(int i = 0; i 《 30; i++)  

      switch((int)(Math。random() * 3)) {  

        case 0 :  

          bin。addElement(new  

            Aluminum(Math。random() * 100));  

          break;  

        case 1 :  

          bin。addElement (new  

            Paper(Math。random() * 100));  

          break;  

        case 2 :  

          bin。addElement(new  

            Glass(Math。random() * 100));  

      }  

  

这些代码显然“过于复杂”,也是新类型加入时必须改动代码的场所之一。如果经常都要加入新类型,那么 

更好的方案就是建立一个独立的方法,用它获取所有必需的信息,并创建一个句柄,指向正确类型的一个对 

象——已经上溯造型到一个Trash 对象。在《Design Patterns》中,它被粗略地称呼为“创建范式”。要在 

这里应用的特殊范式是 Factory 方法的一种变体。在这里,Factory 方法属于 Trash 的一名static (静态) 

成员。但更常见的一种情况是:它属于衍生类中一个被过载的方法。  



                                                                         595 


…………………………………………………………Page 597……………………………………………………………

Factory 方法的基本原理是我们将创建对象所需的基本信息传递给它,然后返回并等候句柄(已经上溯造型 

至基础类型)作为返回值出现。从这时开始,就可以按多形性的方式对待对象了。因此,我们根本没必要知 

道所创建对象的准确类型是什么。事实上,Factory 方法会把自己隐藏起来,我们是看不见它的。这样做可 

防止不慎的误用。如果想在没有多形性的前提下使用对象,必须明确地使用RTTI 和指定造型。  

但仍然存在一个小问题,特别是在基础类中使用更复杂的方法(不是在这里展示的那种),且在衍生类里过 

载(覆盖)了它的前提下。如果在衍生类里请求的信息要求更多或者不同的参数,那么该怎么办呢?“创建 

更多的对象”解决了这个问题。为实现Factory 方法,Trash 类使用了一个新的方法,名为 factory。为了将 

创建数据隐藏起来,我们用一个名为 Info 的新类包含 factory 方法创建适当的 Trash 对象时需要的全部信 

息。下面是 Info 一种简单的实现方式:  

  

class Info {  

  int type;  

  // Must change this to add another type:  

  static final int MAX_NUM = 4;  

  double data;  

  Info(int typeNum; double dat) {  

    type = typeNum % MAX_NUM;  

    data = dat;  

  }  

}  

  

Info 对象唯一的任务就是容纳用于factory()方法的信息。现在,假如出现了一种特殊情况,factory()需要 

更多或者不同的信息来新建一种类型的Trash 对象,那么再也不需要改动factory()了。通过添加新的数据 

和构建器,我们可以修改 Info 类,或者采用子类处理更典型的面向对象形式。  

用于这个简单示例的factory()方法如下:  

  

  static Trash factory(Info i) {  

    switch(i。type) {  

      default: // To quiet the piler  

      case 0:  

        return new Aluminum(i。data);  

      case 1:  

        return new Paper(i。data);  

      case 2:  

        return new Glass(i。data);  

      // Two lines here:  

      case 3:   

        return new Cardboard(i。data);  

    }  

  }  

  

在这里,对象的准确类型很容易即可判断出来。但我们可以设想一些更复杂的情况,factory()将采用一种复 

杂的算法。无论如何,现在的关键是它已隐藏到某个地方,而且我们在添加新类型时知道去那个地方。  

新对象在main()中的创建现在变得非常简单和清爽:  

  

    for(int i = 0; i 《 30; i++)  

      bin。addElement(  

        Trash。factory(  

          new Info(  

            (int)(Math。random() * Info。MAX_NUM);  

            Math。random() * 100)));  

  



                                                                                           596 


…………………………………………………………Page 598……………………………………………………………

我们在这里创建了一个 Info 对象,用于将数据传入 factory() ;后者在内存堆中创建某种Trash 对象,并返 

回添加到Vector bin 内的句柄。当然,如果改变了参数的数量及类型,仍然需要修改这个语句。但假如 

Info 对象的创建是自动进行的,也可以避免那个麻烦。例如,可将参数的一个 Vector 传递到 Info 对象的构 

建器中(或直接传入一个factory()调用)。这要求在运行期间对参数(自变量)进行分析与检查,但确实 

提供了非常高的灵活程度。  

大家从这个代码可看出 Factory 要负责解决的“领头变化”问题:如果向系统添加了新类型(发生了变 

化),唯一需要修改的代码在 Factory 内部,所以Factory 将那种变化的影响隔离出来了。  



16。4。2 用于原型创建的一个范式  



上述设计方案的一个问题是仍然需要一个中心场所,必须在那里知道所有类型的对象:在factory()方法内 

部。如果经常都要向系统添加新类型,factory()方法为每种新类型都要修改一遍。若确实对这个问题感到苦 

恼,可试试再深入一步,将与类型有关的所有信息——包括它的创建过程——都移入代表那种类型的类内 

部。这样一来,每次新添一种类型的时候,需要做的唯一事情就是从一个类继承。  

为将涉及类型创建的信息移入特定类型的 Trash 里,必须使用“原型”(prototype)范式(来自《Design  

Patterns》那本书)。这里最基本的想法是我们有一个主控对象序列,为自己感兴趣的每种类型都制作一 

个。这个序列中的对象只能用于新对象的创建,采用的操作类似内建到Java 根类Object 内部的clone()机 

制。在这种情况下,我们将克隆方法命名为tClone()。准备创建一个新对象时,要事先收集好某种形式的信 

息,用它建立我们希望的对象类型。然后在主控序列中遍历,将手上的信息与主控序列中原型对象内任何适 

当的信息作对比。若找到一个符合自己需要的,就克隆它。  

采用这种方案,我们不必用硬编码的方式植入任何创建信息。每个对象都知道如何揭示出适当的信息,以及 

如何对自身进行克隆。所以一种新类型加入系统的时候,factory()方法不需要任何改变。  

为解决原型的创建问题,一个方法是添加大量方法,用它们支持新对象的创建。但在 Java 1。1 中,如果拥有 

指向Class 对象的一个句柄,那么它已经提供了对创建新对象的支持。利用Java 1。1 的“反射”(已在第 

11章介绍)技术,即便我们只有指向 Class 对象的一个句柄,亦可正常地调用一个构建器。这对原型问题的 

解决无疑是个完美的方案。  

原型列表将由指向所有想创建的Class 对象的一个句柄列表间接地表示。除此之外,假如原型处理失败,则 

factory()方法会认为由于一个特定的Class 对象不在列表中,所以会尝试装载它。通过以这种方式动态装载 

原型,Trash 类根本不需要知道自己要操纵的是什么类型。因此,在我们添加新类型时不需要作出任何形式 

的修改。于是,我们可在本章剩余的部分方便地重复利用它。  

  

//: Trash。java  

// Base class for Trash recycling examples  

package c16。trash;  

import java。util。*;  

import java。lang。reflect。*;  

  

public abstract class Trash {  

  private double weight;  

  Trash(double wt) { weight = wt; }  

  Trash() {}  

  public abstract double value();  

  public double weight() { return weight; }  

  // Sums the value of Trash in a bin:  

  public static void sumValue(Vector bin) {  

    Enumeration e = bin。elements();  

    double val = 0。0f;  

    while(e。hasMoreElements()) {  

      // One kind of RTTI:  

      // A dynamically…checked cast  

      Trash t = (Trash)e。nextElement();  

      val += t。weight() * t。value();  

      System。out。println(  



                                                                                 597 


…………………………………………………………Page 599……………………
返回目录 上一页 下一页 回到顶部 0 0
未阅读完?加入书签已便下次继续阅读!
温馨提示: 温看小说的同时发表评论,说出自己的看法和其它小伙伴们分享也不错哦!发表书评还可以获得积分和经验奖励,认真写原创书评 被采纳为精评可以获得大量金币、积分和经验奖励哦!