按键盘上方向键 ← 或 → 可快速上下翻页,按键盘上的 Enter 键可回到本书目录页,按键盘上方向键 ↑ 可回到本页顶部!
————未阅读完?加入书签已便下次继续阅读!
static double val = 0。23f;
Glass(double wt) { super(wt); }
double value() { return val; }
static void value(double newval) {
val = newval;
}
}
public class RecycleA {
public static void main(String'' args) {
Vector bin = new Vector();
// Fill up the Trash bin:
for(int i = 0; i 《 30; i++)
593
…………………………………………………………Page 595……………………………………………………………
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));
}
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);
}
} ///:~
要注意的第一个地方是 package 语句:
package c16。recyclea;
这意味着在本书采用的源码目录中,这个文件会被置入从 c16 (代表第16 章的程序)分支出来的recyclea
子目录中。第 17 章的解包工具会负责将其置入正确的子目录。之所以要这样做,是因为本章会多次改写这个
特定的例子;它的每个版本都会置入自己的“包”(package)内,避免类名的冲突。
其中创建了几个Vector 对象,用于容纳Trash 句柄。当然,Vector 实际容纳的是 Object (对象),所以它
们最终能够容纳任何东西。之所以要它们容纳 Trash (或者从Trash 衍生出来的其他东西),唯一的理由是
我们需要谨慎地避免放入除Trash 以外的其他任何东西。如果真的把某些“错误”的东西置入Vector,那么
不会在编译期得到出错或警告提示——只能通过运行期的一个违例知道自己已经犯了错误。
Trash 句柄加入后,它们会丢失自己的特定标识信息,只会成为简单的Object 句柄(上溯造型)。然而,由
于存在多形性的因素,所以在我们通过Enumeration sorter 调用动态绑定方法时,一旦结果Object 已经造
型回Trash,仍然会发生正确的行为。sumValue()也用一个Enumeration 对 Vector 中的每个对象进行操作。
表面上持,先把Trash 的类型上溯造型到一个集合容纳基础类型的句柄,再回过头重新下溯造型,这似乎是
一种非常愚蠢的做法。为什么不只是一开始就将垃圾置入适当的容器里呢?(事实上,这正是拨开“回收”
一团迷雾的关键)。在这个程序中,我们很容易就可以换成这种做法,但在某些情况下,系统的结构及灵活
性都能从下溯造型中得到极大的好处。
594
…………………………………………………………Page 596……………………………………………………………
该程序已满足了设计的初衷:它能够正常工作!只要这是个一次性的方案,就会显得非常出色。但是,真正
有用的程序应该能够在任何时候解决问题。所以必须问自己这样一个问题:“如果情况发生了变化,它还能
工作吗?”举个例子来说,厚纸板现在是一种非常有价值的可回收物品,那么如何把它集成到系统中呢(特
别是程序很大很复杂的时候)?由于前面在switch 语句中的类型检查编码可能散布于整个程序,所以每次加
入一种新类型时,都必须找到所有那些编码。若不慎遗漏一个,编译器除了指出存在一个错误之外,不能再
提供任何有价值的帮助。
RTTI 在这里使用不当的关键是“每种类型都进行了测试”。如果由于类型的子集需要特殊的对待,所以只寻
找那个子集,那么情况就会变得好一些。但假如在一个switch 语句中查找每一种类型,那么很可能错过一个
重点,使最终的代码很难维护。在下一节中,大家会学习如何逐步对这个程序进行改进,使其显得越来越灵
活。这是在程序设计中一种非常有意义的例子。
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》中,它被粗略地称呼为“创建范式