Java编程思想第4版[中文版](PDF格式)-第157部分
按键盘上方向键 ← 或 → 可快速上下翻页,按键盘上的 Enter 键可回到本书目录页,按键盘上方向键 ↑ 可回到本页顶部!
————未阅读完?加入书签已便下次继续阅读!
import c16。trash。*;
public class DDCardboard extends Cardboard
implements TypedBinMember {
public DDCardboard(double wt) { super(wt); }
public boolean addToBin(TypedBin'' tb) {
for(int i = 0; i 《 tb。length; i++)
if(tb'i'。add(this))
return true;
return false;
}
} ///:~
每个addToBin()内的代码会为数组中的每个TypeBin 对象调用add()。但请注意参数:this。对Trash 的每
个子类来说,this 的类型都是不同的,所以不能认为代码“完全”一样——尽管以后在 Java 里加入参数化
类型机制后便可认为一样。这是双重派遣的第一个部分,因为一旦进入这个方法内部,便可知道到底是
Aluminum ,Paper,还是其他什么垃圾类型。在对add()的调用过程中,这种信息是通过this 的类型传递
的。编译器会分析出对 add()正确的过载版本的调用。但由于tb'i'会产生指向基础类型 TypeBin 的一个句
柄,所以最终会调用一个不同的方法——具体什么方法取决于当前选择的 TypeBin 的类型。那就是第二次派
遣。
下面是TypeBin 的基础类:
//: TypedBin。java
// Vector that knows how to grab the right type
package c16。doubledispatch;
import c16。trash。*;
import java。util。*;
public abstract class TypedBin {
Vector v = new Vector();
protected boolean addIt(Trash t) {
v。addElement(t);
return true;
}
public Enumeration elements() {
return v。elements();
}
public boolean add(DDAluminum a) {
return false;
609
…………………………………………………………Page 611……………………………………………………………
}
public boolean add(DDPaper a) {
return false;
}
public boolean add(DDGlass a) {
return false;
}
public boolean add(DDCardboard a) {
return false;
}
} ///:~
可以看到,过载的 add()方法全都会返回false。如果未在衍生类里对方法进行过载,它就会一直返回
false,而且调用者(目前是 addToBin())会认为当前Trash 对象尚未成功加入一个集合,所以会继续查找
正确的集合。
在TypeBin 的每一个子类中,都只有一个过载的方法会被过载——具体取决于准备创建的是什么垃圾筒类
型。举个例子来说,CardboardBin 会过载 add(DDCardboard)。过载的方法会将垃圾对象加入它的集合,并返
回true。而CardboardBin 中剩余的所有add()方法都会继续返回 false,因为它们尚未过载。事实上,假如
在这里采用了参数化类型机制,Java 代码的自动创建就要方便得多(使用 C++的“模板”,我们不必费事地
为子类编码,或者将addToBin()方法置入Trash 里;Java 在这方面尚有待改进)。
由于对这个例子来说,垃圾的类型已经定制并置入一个不同的目录,所以需要用一个不同的垃圾数据文件令
其运转起来。下面是一个示范性的 DDTrash。dat:
c16。DoubleDispatch。DDGlass:54
c16。DoubleDispatch。DDPaper:22
c16。DoubleDispatch。DDPaper:11
c16。DoubleDispatch。DDGlass:17
c16。DoubleDispatch。DDAluminum:89
c16。DoubleDispatch。DDPaper:88
c16。DoubleDispatch。DDAluminum:76
c16。DoubleDispatch。DDCardboard:96
c16。DoubleDispatch。DDAluminum:25
c16。DoubleDispatch。DDAluminum:34
c16。DoubleDispatch。DDGlass:11
c16。DoubleDispatch。DDGlass:68
c16。DoubleDispatch。DDGlass:43
c16。DoubleDispatch。DDAluminum:27
c16。DoubleDispatch。DDCardboard:44
c16。DoubleDispatch。DDAluminum:18
c16。DoubleDispatch。DDPaper:91
c16。DoubleDispatch。DDGlass:63
c16。DoubleDispatch。DDGlass:50
c16。DoubleDispatch。DDGlass:80
c16。DoubleDispatch。DDAluminum:81
c16。DoubleDispatch。DDCardboard:12
c16。DoubleDispatch。DDGlass:12
c16。DoubleDispatch。DDGlass:54
c16。DoubleDispatch。DDAluminum:36
c16。DoubleDispatch。DDAluminum:93
c16。DoubleDispatch。DDGlass:93
c16。DoubleDispatch。DDPaper:80
c16。DoubleDispatch。DDGlass:36
610
…………………………………………………………Page 612……………………………………………………………
c16。DoubleDispatch。DDGlass:12
c16。DoubleDispatch。DDGlass:60
c16。DoubleDispatch。DDPaper:66
c16。DoubleDispatch。DDAluminum:36
c16。DoubleDispatch。DDCardboard:22
下面列出程序剩余的部分:
//: DoubleDispatch。java
// Using multiple dispatching to handle more
// than one unknown type during a method call。
package c16。doubledispatch;
import c16。trash。*;
import java。util。*;
class AluminumBin extends TypedBin {
public boolean add(DDAluminum a) {
return addIt(a);
}
}
class PaperBin extends TypedBin {
public boolean add(DDPaper a) {
return addIt(a);
}
}
class GlassBin extends TypedBin {
public boolean add(DDGlass a) {
return addIt(a);
}
}
class CardboardBin extends TypedBin {
public boolean add(DDCardboard a) {
return addIt(a);
}
}
class TrashBinSet {
private TypedBin'' binSet = {
new AluminumBin();
new PaperBin();
new GlassBin();
new CardboardBin()
};
public void sortIntoBins(Vector bin) {
Enumeration e = bin。elements();
while(e。hasMoreElements()) {
TypedBinMember t =
(TypedBinMember)e。nextElement();
if(!t。addToBin(binSet))
611
…………………………………………………………Page 613……………………………………………………………
System。err。println(〃Couldn't add 〃 + t);
}
}
public TypedBin'' binSet() { return binSet; }
}
public class DoubleDispatch {
public static void main(String'' args) {
Vector bin = new Vector();
TrashBinSet bins = new TrashBinSet();
// ParseTrash still works; without changes:
ParseTrash。fillBin(〃DDTrash。dat〃; bin);
// Sort from the master bin into the
// individually…typed bins:
bins。sortIntoBins(bin);
TypedBin'' tb = bins。binSet();
// Perform sumValue for each bin。。。
for(int i = 0; i 《 tb。length; i++)
Trash。sumValue(tb 'i'。v);
// 。。。 and for the master bin
Trash。sumValue(bin);
}
} ///:~
其中,TrashBinSet 封装了各种不同类型的 TypeBin,同时还有 sortIntoBins()方法。所有双重派遣事件都
会在那个方法里发生。可以看到,一旦设置好结构,再归类成各种TypeBin 的工作就变得十分简单了。除此
以外,两个动态方法调用的效率可能也比其他排序方法高一些。
注意这个系统的方便性主要体现在 main()中,同时还要注意到任何特定的类型信息在main()中都是完全独立
的。只与Trash 基础类接口通信的其他所有方法都不会受到Trash 类中发生的改变的干扰。
添加新类型需要作出的改动是完全孤立的:我们随同 addToBin()方法继承Trash 的新类型,然后继承一个新
的TypeBin (这实际只是一个副本,可以简单地编辑),最后将一种新类型加入TrashBinSet 的集合初化化
过程。
16。7 访问器范式
接下来,让我们思考如何将具有完全不同目标的一个设计范式应用到垃圾归类系统。
对这个范式,我们不再关心在系统中加入新型 Trash 时的优化。事实上,这个范式使新型Trash 的添加显得
更加复杂。假定我们有一个基本类结构,它是固定不变的;它或许来自另一个开发者或公司,我们无权对那
个结构进行任何修改。然而,我们又希望在那个结构里加入新的多形性方法。这意味着我们一般必须在基础
类的接口里添加某些东西。因此,我们目前面临的困境是一方面需要向基础类添加方法,另一方面又不能改
动基础类。怎样解决这个问题呢?
“访问器”(Visitor)范式使我们能扩展基本类型的接口,方法是创建类型为Visitor 的一个独立的类结
构,对以后需对基本类型采取的操作进行虚拟。基本类型的任务就是简单地“接收”访问器,然后调用访问
器的动态绑定方法。看起来就象下面这样:
612
…………………………………………………………Page 614……………………………………………………………
现在,假如 v 是一个指向 Aluminum (铝制品)的Visitable 句柄,那么下述代码:
PriceVisitor pv = new PriceVisitor();
v。accept(pv);
会造成两个多形性方法调用:第一个会选择accept()的Aluminum 版本;第二个则在 accept()里——用基础
类Visitor 句柄v 动态调用 visit()的特定版本时。
这种配置意味着可采取 Visitor 的新子类的形式将新的功能添加到系统里,没必要接触Trash 结构。这就是
“访问器”范式最主要的优点:可为一个类结构添加新的多形性功能,同时不必改动结构——只要安装好了
accept()方法。注意这个优点在这儿是有用的,但并不一定是我们在任何情况下的首选方案。所以在最开始
的时候,就要判断这到底是不是自己需要的方案。
现在注意一件没有做成的事情:访问器方案防止了从主控 Trash 序列向单独类型序列的归类。所以我们可将
所有东西都留在单主控序列中,只需用适当的访问器通过那个序列传递,即可达到希望的目标。尽管这似乎
并非访问器范式的本意,但确实让我们达到了很希望达到的一个目标(避免使用RTTI )。
访问器范式中的双生派遣负责同时判断Trash 以及Visitor 的类型。在下面的例子中,大家可看到Visitor
的两种实现方式:PriceVisitor 用于判断总计及价格,而WeightVisitor 用于跟踪重量。
可以看到,所有这些都是用回收程序一个新的、改进过的版本实现的。而且和DoubleDispatch。java 一样,
Trash 类被保持孤立,并创建一个新接口来添加 accept()方法:
613
…………………………………………………………Page 615……………………………………………………………
//: Visitable。java
// An interface to add visitor functionality to
// the Trash hierarchy without modifying the
// base class。
package c16。trashvisitor;
import c16。trash。*;
interface Visitable {
// The new method:
void accept(Visitor v);
} ///:~
Aluminum ,Paper,Glass 以及Cardboard 的子类型实现了accept()方法:
//: VAluminum。java
// Aluminum for the visitor pattern
package c16。trashvisitor;
import c16。trash。*;
public class VAluminum extends Aluminum
implements Visitable {
public VAluminum(double wt) { super(wt); }
public void accept(Visitor v) {
v。visit(this);
}
} ///:~
//: VPaper。java
// Paper for the visitor pattern
package c16。trashvisitor;
import c16。trash。*;
public class VPaper extends Paper
implements Visitable {
public VPaper(double wt) { super(wt); }