论坛首页 Java企业应用论坛

从 JDK 1.0 到 JDK 1.1 中事件模型的发展看继承和组合的优缺点

浏览 14069 次
该帖已经被评为精华帖
作者 正文
   发表时间:2003-11-07  
JDK 1.0 事件模型向 JDK 1.1 事件模型的发展,反映了 OOP 从以继承为中心向以接口为中心的转变。《设计模式》等一系列面向对象经典教材一再教导我们,要针对接口编程,不要针对实现编程。要优先使用组合,慎重使用继承。
先来看一下 JDK 1.0 中的事件模型。
1、在 JDK 1.0 中,所有的事件处理由 GUI 组件(Window、Menu、Button、etc.)本身负责。这些组件都有一个 action() 方法,传进来一个 Event 对象。这样要实现特殊的事件处理,就要为这些组件生成子类,然后覆盖掉 action 方法。对于 Container 类的对象是覆盖掉 handleEvent() 方法。如果子类的 handleEvent() 写得有问题,比如没有调用父类的 handleEvent(),就会造成有些事件无法得到处理的问题。
2、通过 action() 或 handleEvent() 方法传进来 Event 对象究竟是什么事件,不知道,需要再写一个长长的 switch 或者 if...else if...else 来判断。这不是面向对象的开发方式。
3、性能较差。JDK 1.0 的事件模型采用冒泡模型处理事件。在多个深度嵌套的组件中产生事件时,这些事件会冒泡到最外层容器中,由容器集中处理所有事件,这个冒泡过程会极大增加程序的冗余度。
4、将对事件所做的逻辑处理代码与 GUI 组件本身的显示代码混杂在一起。对于开发大的系统非常不利。

JDK 1.1 通过将继承关系修改为组合关系彻底改变了这一切。
JDK 1.1 中每个事件需要在 GUI 组件中注册一个 Listener,这个 Listener 和 GUI 组件是组合的关系。当有事件发生,GUI 组件调用 Listener 的一个方法(如 actionPerformed())将发生的事件传递给 Listener,由 Listener 在自己的代码片中处理,完全解决了 JDK 1.0 存在的上述问题。
1、不必再生成子类,因为现在使用的是组合而不是继承。
2、不用写 switch 或 if...else if...else 判断 Event 的类型,因为只会传给那个特定的 Listener 它所需要的特定 Event。
3、事件不用再冒泡,提高了性能。
4、逻辑处理的代码与显示的代码完全分离,实现了更好的封装。

更大的好处是增加了灵活性,使得为组件增加新的事件处理代码只需要修改很少的代码,实现了更大程度的重用。
   发表时间:2003-11-08  
我觉得你有一味打击继承,提倡组合的倾向

继承与组合各有各的用处,这要看在什么场合中了
0 请登录后投票
   发表时间:2003-11-08  
jinbo 写道
我觉得你有一味打击继承,提倡组合的倾向 

继承与组合各有各的用处,这要看在什么场合中了

这正是我们需要讨论的问题。不要撂下一句话就走啊,呵呵。
面向对象开发以接口为中心展开设计早已经是不争的事实。尤其是各种开发框架,充分体现了 Design by Contract 的思想。这个 Contract 由什么来表达?主要就是由接口来表达。接口可大可小,可以封装一个子系统,也可以只封装一个简单的类。以前我做过长期的电信协议开发,电信协议分层分得很清楚,相邻层之间通过格式严格定义的原语来进行通信。每个协议层可以接受的原语(数据和指令)就是这个协议层对外所表现的接口,而接收到特定的原语后所执行的操作(还涉及到发送相关的原语)和发生的状态迁移则是协议层本身的内部行为。
仔细分析 GOF 23 种设计模式,在叹服其设计的精巧之外,你会发现大多数模式都是以接口为中心展开讨论的(核心是组合而非继承),继承只是起到了辅助的作用,而且继承的层次一般不会超过两层,不会出现 MFC 中继承很多层的情况。

接口的思想不仅仅体现在 OOP 中,数据库设计同样是接口设计。如果数据库没有设计好,依靠高超的编程能力仍然无法解决复杂的业务问题。这就是我强调首先要做好数据库设计的原因。数据库设计就是针对接口做开发。首先把接口和协议定义好,然后再做与实现相关的工作,这是面向对象所教给我们的正确的思考方法。

我的观点是在我读了《设计模式》这些书之后自然产生的推论,如果有错误,只能说明 GOF 等人给我带来的毒害太深,不过我目前对他们还是很迷信的。
1 请登录后投票
   发表时间:2003-11-08  
现在我的感觉是:
需要用模板模式的地方用继承,但只从抽象类继承,这主要是为了使用回调接口

工具类使用有限的具体继承
这是因为工具类的适用面比较窄,适度的使用具体继承能减轻工作量,这也是遵守20/80原则,不必在这个问题域追求多完美的设计,因为在这上面的投资只能换来一个特定问题的解决方法。当然,这是指一些助手类。总之就是,小规模的问题,用用具体继承也无妨
其余用接口,尽量做到面向接口编程

使用接口唯一的缺点是编码量变大,我正在考虑看看能否用代码生成技术减轻这个负担,因为好多代码都是重复的,ibaits用的就是这种策略
0 请登录后投票
   发表时间:2003-11-08  
无明 写道
使用接口唯一的缺点是编码量变大,我正在考虑看看能否用代码生成技术减轻这个负担,因为好多代码都是重复的,ibaits用的就是这种策略

编码量变大是不必担心的。接口仅仅是系统的一个骨架,一个复杂的系统仅仅有这样一个骨架是绝对不够的。那样灵活性是做到了,但是易用性又无法满足了。好在框架的设计者在开发框架时已经充分考虑了这个问题。所以一些常用的接口都会开发出实现这些接口的适配器类。这些适配器类就是通用的解决方案。如果你需要特殊的解决方案可以继承这些适配器类,然后覆盖掉不符合要求的方法。
Java 里面这样的例子是很多的。AWT 中就有大量这样的 Adapter 类。
1 请登录后投票
   发表时间:2003-11-08  
Java的UI部分我几乎没涉及:(

如果是接口->Adapter->覆盖方法的做法的话,那设计者的意图恐怕就是尽量缩小问题域,在一个很狭窄的地方使用具体继承

接口继承——>类型的继承
具体继承——>方法的继承
0 请登录后投票
   发表时间:2003-11-09  
无明 写道
Java的UI部分我几乎没涉及:(

JDK 1.1 以后的事件模型发展到现在也没有什么变化。Java 的事件模型与 GUI(AWT/Swing)是完全无关的,只是在 GUI 应用中用的比较多。完全可以脱离开 GUI 实现自己的事件类型和事件监听器类型。

JavaBean 是我见过的最容易理解的组件模型,比起 M$ ActiveX 那些破烂货要容易理解得多,而且也灵活的多。JavaBean+Java 事件模型完全起到了一巧破千斤的效果。从面向对象的角度 Java 其实是最适合做 GUI 开发的语言,之所以没有普及只是受限于性能问题。不过目前的主流机器(P4 CPU,256M 以上内存)上已经没有任何问题了。使用 Java 来开发 IDE 已经是一个潮流,听说 C++ Builder 下一版本是用 Java 开发的。
0 请登录后投票
   发表时间:2003-11-09  
有点跑题了,不如开新贴讨论一下Java的事件模型:
http://forum.hibernate.org.cn/viewtopic.php?p=5878#5878
0 请登录后投票
   发表时间:2003-11-11  
接口的使用在设计模式中说得很多,但我觉得他们的说法是针对继承被过度使用而说的,比如就有人问过我在以前的项目中类有多少层。 类的层次多少可以用过比较设计是否合理么?8) 

但继承在某些场合还是有必要的,比如需要强制要求子类必须执行某些操作,或符合某些约定等等。

JAVA中全部的类必须由Object中继承就是继承的一个典型应用,这也是接口无法解决的应用了
0 请登录后投票
   发表时间:2003-11-11  
jinbo 写道
类的层次多少可以用过比较设计是否合理么?

我认为是可以的。这可以作为一个简单的衡量标准。继承可以做的事采用接口、组合配合适当的设计模式都是可以做的。你可以写一段完全使用继承的代码,我用设计模式帮你改写。
继承的层次越多,系统与具体实现的耦合越紧,越难以实现重用。我的看法是最好不要使用超过 3 层的继承关系。
继承的缺点是前人在做过大量开发实践后发现的。就象 goto 的缺点一样,有人争论在某些场合使用 goto 有好处我也不反对。这是一个普遍规律和特殊规律的关系。
jinbo 写道
JAVA中全部的类必须由Object中继承就是继承的一个典型应用,这也是接口无法解决的应用了

我从来没有说过接口可以解决所有的问题。接口解决的是体系结构的设计问题,只是搭出一个系统的骨架,具体的实现(系统的血肉)是与接口无关的。实现的时候要慎重使用继承,尽量多采用组合和设计模式。
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics