友情提示:如果本网页打开太慢或显示不完整,请尝试鼠标右键“刷新”本网页!
Java编程思想第4版[中文版](PDF格式)-第143部分
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部! 如果本书没有阅读完,想下次继续接着阅读,可使用上方 "收藏到我的浏览器" 功能 和 "加入书签" 功能!
正确的集合。
在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); }
public void accept(Visitor v) {
v。visit(this);
}
} ///:~
//: VGlass。java
// Glass for the visitor pattern
package c16。trashvisitor;
import c16。trash。*;
public class VGlass extends Glass
implements Visitable {
public VGlass(double wt) { super(wt); }
public void accept(Visitor v) {
v。visit(this);
}
} ///:~
//: VCardboard。java
614
…………………………………………………………Page 616……………………………………………………………
// Cardboard for the visitor pattern
package c16。trashvisitor;
import c16。trash。*;
public class VCardboard extends Cardboard
implements Visitable {
public VCardboard(double wt) { super(wt); }
public void accept(Visitor v) {
v。visit(this);
}
} ///:~
由于Visitor 基础类没有什么需要实在的东西,可将其创建成一个接口:
//: Visitor。java
// The base interface for visitors
package c16。trashvisitor;
import c16。trash。*;
interface Visitor {
void visit(VAluminum a);
void visit(VPaper p);
void visit(VGlass g);
void visit(VCardboard c);
} ///:~
Once again custom Trash types have been created in a different subdirectory。 The new Trash data
file is VTrash。dat and looks like this:
c16。TrashVisitor。VGlass:54
c16。TrashVisitor。VPaper:22
c16。TrashVisitor。VPaper:11
c16。TrashVisitor。VGlass:17
c16。TrashVisitor。VAluminum:89
c16。TrashVisitor。VPaper:88
c16。TrashVisitor。VAluminum:76
c16。TrashVisitor。VCardboard:96
c16。TrashVisitor。VAluminum:25
c16。TrashVisitor。VAluminum:34
c16。TrashVisitor。VGlass:11
c16。TrashVisitor。VGlass:68
c16。TrashVisitor。VGlass:43
c16。TrashVisitor。VAluminum:27
c16。TrashVisitor。VCardboard:44
c16。TrashVisitor。VAluminum:18
c16。TrashVisitor。VPaper:91
c16。TrashVisitor。VGlass:63
c16。TrashVisitor。VGlass:50
c16。TrashVisitor。VGlass:80
c16。TrashVisitor。VAluminum:81
c16。TrashVisitor。VCardboard:12
c16。TrashVisitor。VGlass:12
615
…………………………………………………………Page 617……………………………………………………………
c16。TrashVisitor。VGlass:54
c16。TrashVisitor。VAluminum:36
c16。TrashVisitor。VAluminum:93
c16。TrashVisitor。VGlass:93
c16。TrashVisitor。VPaper:80
c16。TrashVisitor。VGlass:36
c16。TrashVisitor。VGlass:12
c16。TrashVisitor。VGlass:60
c16。TrashVisitor。VPaper:66
c16。TrashVisitor。VAluminum:36
c16。TrashVisitor。VCardboard:22
程序剩余的部分将创建特定的 Visitor 类型,并通过一个 Trash 对象列表发送它们:
//: TrashVisitor。java
// The 〃visitor〃 pattern
package c16。trashvisitor;
import c16。trash。*;
import java。util。*;
// Specific group of algorithms packaged
// in each implementation of Visitor:
class PriceVisitor implements Vis
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部!
温馨提示: 温看小说的同时发表评论,说出自己的看法和其它小伙伴们分享也不错哦!发表书评还可以获得积分和经验奖励,认真写原创书评 被采纳为精评可以获得大量金币、积分和经验奖励哦!