79.JAVA编程思想——抽象应用

79.JAVA编程思想——抽象应用

1     抽象应用

接下来该考虑一下设计方案剩下的部分了——在哪里使用类?既然归类到垃圾箱的办法非常不雅且过于暴露,为什么不隔离那个过程,把它隐藏到一个类里呢?这就是著名的“如果必须做不雅的事情,至少应将其本地化到一个类里”规则。

现在,只要一种新类型的Trash 加入方法,对TrashSorter 对象的初始化就必须变动。可以想象,TrashSorter 类看起来应该象下面这个样子:

class TrashSorter extends Vector{

void sort(Trash t) { /* ... */ }

}

也就是说,TrashSorter 是由一系列句柄构成的Vector(系列),而那些句柄指向的又是由Trash 句柄构成的Vector;利用addElement(),可以安装新的TrashSorter,如下所示:

TrashSorter ts = newTrashSorter();

ts.addElement(new Vector());

但是现在,sort()却成为一个问题。用静态方式编码的方法如何应付一种新类型加入的事实呢?为解决这个问题,必须从sort()里将类型信息删除,使其需要做的所有事情就是调用一个通用方法,用它照料涉及类型处理的所有细节。这当然是对一个动态绑定方法进行描述的另一种方式。所以sort()会在序列中简单地遍历,并为每个Vector 都调用一个动态绑定方法。由于这个方法的任务是收集它感兴趣的垃圾片,所以称之为grab(Trash)。结构现在变成了下面这样:

其中,TrashSorter 需要调用每个grab()方法;然后根据当前Vector 容纳的是什么类型,会获得一个不同的结果。也就是说,Vector 必须留意自己容纳的类型。解决这个问题的传统方法是创建一个基础“Trashbin”(垃圾筒)类,并为希望容纳的每个不同的类型都继承一个新的衍生类。若Java 有一个参数化的类型机制,那就也许是最直接的方法。但对于这种机制应该为我们构建的各个类,我们不应该进行麻烦的手工编码,以后的“观察”方式提供了一种更好的编码方式。

OOP 设计一条基本的准则是“为状态的变化使用数据成员,为行为的变化使用多性形”。对于容纳Paper(纸张)的Vector,以及容纳Glass(玻璃)的Vector,大家最开始或许会认为分别用于它们的grab()方法肯定会产生不同的行为。但具体如何却完全取决于类型,而不是其他什么东西。可将其解释成一种不同的状态,

而且由于Java 有一个类可表示类型(Class),所以可用它判断特定的Tbin 要容纳什么类型的Trash。

用于Tbin 的构建器要求我们为其传递自己选择的一个Class。这样做可告诉Vector 它希望容纳的是什么类型。随后,grab()方法用Class BinType 和RTTI 来检查我们传递给它的Trash 对象是否与它希望收集的类型相符。下面列出完整的解决方案。设定为注释的编号(如*1*)便于大家对照程序后面列出的说明。

2     多重派遣

上述设计方案肯定是令人满意的。系统内新类型的加入涉及添加或修改不同的类,但没有必要在系统内对代码作大范围的改动。除此以外,RTTI 并不被不当地使用。然而,我们仍然有可

能更深入一步,以最“纯”的角度来看待RTTI,考虑如何在垃圾分类系统中将它完全消灭。为达到这个目标,首先必须认识到:对所有与不同类型有特殊关联的活动来说——比如侦测一种垃圾的具体类型,并把它置入适当的垃圾筒里——这些活动都应当通过多形性以及动态绑定加以控制。以前的例子都是先按类型排序,再对属于某种特殊类型的一系列元素进行操作。现在一旦需要操作特定的类型,就请先停下来想一想。事实上,多形性(动态绑定的方法调用)整个的宗旨就是帮我们管理与不同类型有特殊关联的信息。既然如此,为什么还要自己去检查类型呢?

答案在于大家或许不以为然的一个道理:Java 只执行单一派遣。也就是说,假如对多个类型未知的对象执行某项操作,Java 只会为那些类型中的一种调用动态绑定机制。这当然不能解决问题,所以最后不得不人工判断某些类型,才能有效地产生自己的动态绑定行为。

为解决这个缺陷,我们需要用到“多重派遣”机制,这意味着需要建立一个配置,使单一方法调用能产生多个动态方法调用,从而在一次处理过程中正确判断出多种类型。为达到这个要求,需要对多个类型结构进行操作:每一次派遣都需要一个类型结构。下面的例子将对两个结构进行操作:现有的Trash 系列以及由垃圾筒(Trash Bin)的类型构成的一个系列——不同的垃圾或废品将置入这些筒内。第二个分级结构并非绝对显然的。在这种情况下,我们需要人为地创建它,以执行多重派遣(由于本例只涉及两次派遣,所以称为“双重派遣”)。

2.1     实现双重派遣

记住多形性只能通过方法调用才能表现出来,所以假如想使双重派遣正确进行,必须执行两个方法调用:在每种结构中都用一个来判断其中的类型。在Trash 结构中,将使用一个新的方法调用addToBin(),它采用的参数是由TypeBin 构成的一个数组。那个方法将在数组中遍历,尝试将自己加入适当的垃圾筒,这里正是双重派遣发生的地方。

新建立的分级结构是TypeBin,其中包含了它自己的一个方法,名为add(),而且也应用了多形性。但要注意一个新特点:add()已进行了“过载”处理,可接受不同的垃圾类型作为参数。因此,双重满足机制的一个关键点是它也要涉及到过载。

程序的重新设计也带来了一个问题:现在的基础类Trash 必须包含一个addToBin()方法。为解决这个问题,一个最直接的办法是复制所有代码,并修改基础类。然而,假如没有对源码的控制权,那么还有另一个办法可以考虑:将addToBin()方法置入一个接口内部,保持Trash 不变,并继承新的、特殊的类型Aluminum,Paper,Glass以及Cardboard。我们在这里准备采取后一个办法。

3     访问器范式

接下来,让我们思考如何将具有完全不同目标的一个设计范式应用到垃圾归类系统。对这个范式,我们不再关心在系统中加入新型Trash 时的优化。事实上,这个范式使新型Trash 的添加显得更加复杂。假定我们有一个基本类结构,它是固定不变的;它或许来自另一个开发者或公司,我们无权对那个结构进行任何修改。然而,我们又希望在那个结构里加入新的多形性方法。这意味着我们一般必须在基础类的接口里添加某些东西。因此,我们目前面临的困境是一方面需要向基础类添加方法,另一方面又不能改动基础类。怎样解决这个问题呢?

“访问器”(Visitor)范式使我们能扩展基本类型的接口,方法是创建类型为Visitor 的一个独立的类结构,对以后需对基本类型采取的操作进行虚拟。基本类型的任务就是简单地“接收”访问器,然后调用访问器的动态绑定方法。看起来就象下面这样:

1. 更多的结合?

这里还有其他许多代码,Trash 结构和Visitor 结构之间存在着明显的“结合”(Coupling)关系。然而,在它们所代表的类集内部,也存在着高度的凝聚力:都只做一件事情(Trash 描述垃圾或废品,而Visitor描述对垃圾采取什么行动)。作为一套优秀的设计方案,这无疑是个良好的开端。当然就目前的情况来说,只有在我们添加新的Visitor 类型时才能体会到它的好处。但在添加新类型的Trash 时,它却显得有些碍手碍脚。

类与类之间低度的结合与类内高度的凝聚无疑是一个重要的设计目标。但只要稍不留神,就可能妨碍我们得到一个本该更出色的设计。从表面看,有些类不可避免地相互间存在着一些“亲密”关系。这种关系通常是成对发生的,可以叫作“对联”(Couplet)——比如集合和继承器(Enumeration)。前面的Trash-Visitor 对似乎也是这样的一种“对联”。

4     R T T I 真的有害吗

新类型向系统的加入根本不会影响到这些代码,亦不会影响TypeMap 中的代码。这显然是解决问题最圆满的方案。尽管它确实严重依赖RTTI,但请注意散列表中的每个键-值对都只查找一种类型。除此以外,在我们增加一种新类型的时候,不会陷入“忘记”向系统加入正确代码的尴尬境地,因为根本就没有什么代码需要添加。

5     总结

从表面看,由于象TrashVisitor.java 这样的设计包含了比早期设计数量更多的代码,所以会留下效率不高的印象。试图用各种设计方案达到什么目的应该是我们考虑的重点。设计范式特别适合“将发生变化的东西与保持不变的东西隔离开”。而“发生变化的东西”可以代表许多种变化。之所以发生变化,可能是由于程序进入一个新环境,或者由于当前环境的一些东西发生了变化(例如“用户希望在屏幕上当前显示的图示中添加一种新的几何形状”)。或者就象本章描述的那样,变化可能是对代码主体的不断改进。尽管废品分类以前的例子强调了新型Trash
向系统的加入,但TrashVisitor.java 允许我们方便地添加新功能,同时不会对Trash 结构造成干扰。TrashVisitor.java 里确实多出了许多代码,但在Visitor 里添加新功能只需要极小的代价。如果经常都要进行此类活动,那么多一些代码也是值得的。

变化序列的发现并非一件平常事;在程序的初始设计出台以前,那些分析家一般不可能预测到这种变化。除非进入项目设计的后期,否则一些必要的信息是不会显露出来的:有时只有进入设计或最终实现阶段,才能体会到对自己系统一个更深入或更不易察觉需要。添加新类型时(这是“回收”例子最主要的一个重点),可能会意识到只有自己进入维护阶段,而且开始扩充系统时,才需要一个特定的继承结构。通过设计范式的学习,大家可体会到最重要的一件事情就是本书一直宣扬的一个观点——多形性是OOP(面向对象程序设计)的全部——已发生了彻底的改变。换句话说,很难“获得”多形性;而一旦获得,就需要尝试将自己的所有设计都造型到一个特定的模子里去。

设计范式要表明的观点是“OOP 并不仅仅同多形性有关”。应当与OOP 有关的是“将发生变化的东西同保持不变的东西分隔开来”。多形性是达到这一目的的特别重要的手段。而且假如编程语言直接支持多形性,那么它就显得尤其有用(由于直接支持,所以不必自己动手编写,从而节省大量的精力和时间)。但设计范式向我们揭示的却是达到基本目标的另一些常规途径。而且一旦熟悉并掌握了它的用法,就会发现自己可以做出更有创新性的设计。

由于《Design Patterns》这本书对程序员造成了如此重要的影响,所以他们纷纷开始寻找其他范式。随着的时间的推移,这类范式必然会越来越多。JimCoplien(http://www.bell-labs.com/~cope 主页作者)向我们

推荐了这样的一些站点,上面有许多很有价值的范式说明:

http://st-www.cs.uiuc.edu/users/patterns

http://c2.com/cgi/wiki

http://c2.com/ppr

http://www.bell-labs.com/people/cope/Patterns/Process/index.html

http://www.bell-labs.com/cgi-user/OrgPatterns/OrgPatterns

http://st-www.cs.uiuc.edu/cgi-bin/wikic/wikic

http://www.cs.wustl.edu/~schmidt/patterns.html

621

http://www.espinc.com/patterns/overview.html

同时请留意每年都要召开一届权威性的设计范式会议,名为PLOP。会议会出版许多学术论文,第三届已在1997 年底召开过了,会议所有资料均由Addison-Wesley 出版。

时间: 2025-01-08 18:30:55

79.JAVA编程思想——抽象应用的相关文章

《Java编程思想(第4版)》pdf

下载地址:网盘下载 内容简介 编辑 本书赢得了全球程序员的广泛赞誉,即使是最晦涩的概念,在Bruce Eckel的文字亲和力和小而直接的编程示例面前也会化解于无形.从Java的基础语法到最高级特性(深入的面向对象概念.多线程.自动项目构建.单元测试和调试等),本书都能逐步指导你轻松掌握.[1] 从本书获得的各项大奖以及来自世界各地的读者评论中,不难看出这是一本经典之作.本书的作者拥有多年教学经验,对C.C++以及Java语言都有独到.深入的见解,以通俗易懂及小而直接的示例解释了一个个晦涩抽象的概

Java编程思想重点笔记(Java开发必看)

Java编程思想,Java学习必读经典,不管是初学者还是大牛都值得一读,这里总结书中的重点知识,这些知识不仅经常出现在各大知名公司的笔试面 试过程中,而且在大型项目开发中也是常用的知识,既有简单的概念理解题(比如is-a关系和has-a关系的区别),也有深入的涉及RTTI和JVM底层 反编译知识. 1. Java中的多态性理解(注意与C++区分) Java中除了static方法和final方法(private方法本质上属于final方法,因为不能被子类访问)之外,其它所有的方法都是动态绑定,这意

1.JAVA 编程思想——对象入门

对象入门 欢迎转载,转载请标明出处:    http://blog.csdn.net/notbaron/article/details/51040219 如果学JAVA,没有读透<JAVA 编程思想>这本书,实在不好意思和别人说自己学过JAVA.鉴于此,蛤蟆忙里偷闲,偷偷翻看这本传说中的牛书. 面向对象编程OOP具有多方面吸引力.实现了更快和更廉价的开发与维护过程.对分析与设计人员,建模处理变得更加简单,能生成清晰.已于维护的设计方案. 这些描述看上去非常吸引人的,不过蛤蟆还是没啥印象(至少到

【java编程思想--学习笔记(四)】对象导论

写这篇博客的前言: 长话短说,我希望通过阅读<java编程思想>来使我的代码 简洁可用 . 目的的层次不同,首先具体的目标是,了解Java的特性和巩固Java的基础. 更抽象的目的如下: 1.期待以巩固基础的方式,使代码优美,简洁,高效. 2.使自己写的模块能够开放适度,好用. 3.形成一种对代码是否优美的审美观. 于是<Java编程思想>第一章 对象导论 由此开始. 1.1 抽象过程 java 相对于命令式语言的优势在于只针对于待解问题建模.后者所做的主要抽象要求所做问题基于计算

Java编程思想第四版读书笔记——第十三章 字符串

Java编程思想第四版读书笔记--第十三章 字符串 字符串的操作是计算机程序设计中最常见的行为. 关键词: StringBuilder ,StringBuffer,toString(),format转换,正则表达式, 1.不可变String String对象时不可变的.每当把String对象作为方法的参数时,都会复制一份引用.(其实就是对函数中参数列表中参数的操作不会影响外面的原参数) 如下: import static net.mindview.util.Print.*; public cla

66.JAVA编程思想——网络编程

66.JAVA编程思想--网络编程 历史上的网络编程都倾向于困难.复杂,而且极易出错. 程序员必须掌握与网络有关的大量细节,有时甚至要对硬件有深刻的认识.一般地,我们需要理解连网协议中不同的"层"(Layer).而且对于每个连网库,一般都包含了数量众多的函数,分别涉及信息块的连接.打包和拆包:这些块的来回运输:以及握手等等.这是一项令人痛苦的工作.但是,连网本身的概念并不是很难.我们想获得位于其他地方某台机器上的信息,并把它们移到这儿:或者相反.这与读写文件非常相似,只是文件存在于远程

39.JAVA编程思想之外篇——JAVA图形化设计精简大全一文覆盖

39.JAVA编程思想之外篇--JAVA图形化设计精简大全一文覆盖 欢迎转载,转载请标明出处:http://blog.csdn.net/notbaron/article/details/51204948 目录 Java图形化界面设计--容器(JFrame)...1 Java基本类(JFC)...1 l     AWTAbstract Window Toolkit(AWT)抽象窗口工具包... 2 l     Swing包... 2 l     AWT和Swing的区别... 6 Swing基本框

java编程思想总结(三)

java编程思想总结(三) java编程思想总结是一个持续更新的系列,是本人对自己多年工作中使用到java的一个经验性总结,也是温故而知新吧,因为很多基础的东西过了这么多年,平时工作中用不到也会遗忘掉,所以看看书,上上网,查查资料,也算是记录下自己的笔记吧,过一段时间之后再来看看也是蛮不错的,也希望能帮助到正在学习的人们,本系列将要总结一下几点: 面向对象的编程思想 java的基本语法 一些有趣的框架解析 实战项目的整体思路 代码的优化以及性能调优的几种方案 整体项目的规划和视角 其它遗漏的东西

什么是JAVA编程思想?

什么是JAVA编程思想?答案可能很会复杂,但也可以很简单.要了解JAVA编程思想,首先就要了解什么是编程思想,让我们来看看什么是编程思想,一句话来讲就是,用计算机来解决人们实际问题的思维方式,即编程思想. 我们学习编程语言的最终目的,就是希望用计算机来解决我们的实际问题.那么学习编程该如何入手,也是很多初学者犯难的一个问题,特别是对与非计算机专业的人来说更是如此.面对现实如此多的编程语言(比如:C,C++,JAVA,C# -)和 种类繁多的应用技术(比如: windows编程, linux编程,