Java理论与实践:做个好的(事件)侦听器 - 编程入门网
Java理论与实践:做个好的(事件)侦听器时间:2010-12-20 IBM Brian Goetz观察者模式在 Swing 开发中很常见,在 GUI 应用程序以外的场景中,它对 于消除组件的耦合性也非常有用。但是,仍然存在一些侦听器登记和调用方面的 常见缺陷。在 Java 理论与实践 的这一期中,Java 专家 Brian Goetz 就如何 做一个好的侦听器,以及如何对您的侦听器也友好,提供了一些感觉很好的建议 。请在相应的 讨论论坛 上与作者和其他读者分享您对这篇文章的想法。(您也 可以单击本文顶部或底部的 讨论 访问论坛。) Swing 框架以事件侦听器的形式广泛利用了观察者模式(也称为发布-订阅模 式)。Swing 组件作为用户交互的目标,在用户与它们交互的时候触发事件;数 据模型类在数据发生变化时触发事件。用这种方式使用观察者,可以让控制器与 模型分离,让模型与视图分离,从而简化 GUI 应用程序的开发。 “四人帮”的 设计模式 一书(参阅 参考资料)把观察者模式描述为:定义 对象之间的“一对多”关系,这样一个对象改变状态时,所有它的依赖项都会被 通知,并自动更新。观察者模式支持组件之间的松散耦合;组件可以保持它们的 状态同步,却不需要直接知道彼此的标识或内部情况,从而促进了组件的重用。 AWT 和 Swing 组件(例如 JButton 或 JTable)使用观察者模式消除了 GUI 事件生成与它们在指定应用程序中的语义之间的耦合。类似地,Swing 的模型类 ,例如 TableModel 和 TreeModel,也使用观察者消除数据模型表示 与视图生 成之间的耦合,从而支持相同数据的多个独立的视图。Swing 定义了 Event 和 EventListener 对象层次结构;可以生成事件的组件,例如 JButton(可视组件 ) 或 TableModel(数据模型),提供了 addXxxListener() 和 removeXxxListener() 方法,用于侦听器的登记和取消登记。这些类负责决定什 么时候它们需要触发事件,什么时候确实触发事件,以及什么时候调用所有登记 的侦听器。 为了支持侦听器,对象需要维护一个已登记的侦听器列表,提供侦听器登记 和取消登记的手段,并在适当的事件发生时调用每个侦听器。使用和支持侦听器 很容易(不仅仅在 GUI 应用程序中),但是在登记接口的两边(它们是支持侦 听器的组件和登记侦听器的组件)都应当避免一些缺陷。 线程安全问题 通常,调用侦听器的线程与登记侦听器的线程不同。要支持从不同线程登记 侦听器,那么不管用什么机制存储和管理活动侦听器列表,这个机制都必须是线 程安全的。Sun 的文档中的许多示例使用 Vector 保存侦听器列表,它解决了部 分问题,但是没有解决全部问题。在事件触发时,触发它的组件会考虑迭代侦听 器列表,并调用每个侦听器,这就带来了并发修改的风险,比如在侦听器列表迭 代期间,某个线程偶然想添加或删除一个侦听器。 管理侦听器列表 假设您使用 Vector<Listener> 保存侦听器列表。虽然 Vector 类是 线程安全的(意味着不需要进行额外的同步就可调用它的方法,没有破坏 Vector 数据结构的风险),但是集合的迭代中包含“检测然后执行”序列,如 果在迭代期间集合被修改,就有了失败的风险。假设迭代开始时列表中有三个侦 听器。在迭代 Vector 时,重复调用 size() 和 get() 方法,直到所有元素都 检索完,如清单 1 所示: 清单 1. Vector 的不安全迭代 但是,如果恰好就在最后一次调用 Vector.size() 之后,有人从列表中删除 了一个侦听器,会发生什么呢?现在,Vector.get() 将返回 null (这是对的 ,因为从上次检测 vector 的状态以来,它的状态已经变了),而在试图调用 eventHa |
凌众科技专业提供服务器租用、服务器托管、企业邮局、虚拟主机等服务,公司网站:http://www.lingzhong.cn 为了给广大客户了解更多的技术信息,本技术文章收集来源于网络,凌众科技尊重文章作者的版权,如果有涉及你的版权有必要删除你的文章,请和我们联系。以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢! |