观察者模式在Java 事件处理中的应用研究*
2013-07-20欧阳宏基杨卫忠
欧阳宏基,杨卫忠,赵 蔷
(1.咸阳师范学院信息工程学院,咸阳 712000;2.陕西省高速公路建设集团公司服务区管理分公司,西安 710061)
1 引言
Erich Gamma 等人在20世纪90年代出版的《Design Patterns:Elements of Reusable Object- Oriented Software》一书中将设计模式的概念从建筑学领域引入到了计算机软件领域。此书总结了在面向对象软件开发中所常用的23种设计模式,并将其归纳为三种类型:创建型、行为型和结构型[1]。从软件领域角度讲,设计模式就是以面向对象的软件实践过程中所重复出现的、但本质和解决方法十分类似的问题的归纳总结,从思想的高度展示了接口和抽象类在实际案例中的灵活应用[2]。在面向对象的软件开发中应用设计模式能够使系统易于维护、扩展和复用。
Observer 模式(观察者模式)是行为型模式的一种典型代表,该模式的应用场景是:对象之间存在一种一对多的依赖关系,当一个对象的状态发生变化时,所有依赖它的对象都得到通知并被自动更新状态或执行相应的操作。Observer 模式在Java JDK 中的典型应用就是异常处理机制和AWT 中的事件处理机制。分析了Observer 模式的各组成部分并将其应用到AWT的事件处理机制中,根据被观察者与观察者对象的位置关系,给出了三种具体完成事件处理机制的方案并分析了它们的优缺点。
2 Observer 模式
Observer 模式是关于多个对象想知道一个对象中数据变化情况的一种成熟模式[3]。其中有一个称作“被观察者”对象和若干个称作“观察者”对象。“被观察者”与“观察者”是一对多的依赖关系,当“被观察者”的状态发生变化时,所有“观察者”都得到通知并执行相应的操作。Observer 模式的结构中包括四种角色,它们之间的关系如图1 所示:
(1)被观察者接口(Target):该接口定义了具体被观察者需要实现的方法。例如:添加、删除观察者以及通知观察者更新数据的方法。
(2)观察者接口(Observer):该接口定义了具体观察者用来更新数据的方法,当被观察者发出更新通知时,及时地更新自己,与被观察者保持一致[4]。
(3)具体被观察者(ConcreteTarget):具体被观察者实现了被观察者接口,该类中包含有可以经常发生变化的数据和一个存放所有观察者对象引用的集合,当数据发生变化时会通知集合中的每一个观察者。
(4)具体观察者(ConcreteObserver):具体观察者实现了观察者接口,该类中包含了一个存放具体被观察者对象的被观察者接口变量,以便具体观察者让具体被观察者将自己的引用添加到观察者的集合中,使自己成为它的观察者。或者让被观察者将自己从观察者集合中删除,不再担当观察者的任务。具体观察者还要包含当接收到具体被观察者状态更新通知后要执行的操作。
图1 Observer 模式类图关系
3 Java 事件处理机制
java.awt 包和javax.swing 包提供了利用Java API 创建图形用户界面(GUI)的功能。通常触发一个组件会产生相应的事件(例如点击界面上的一个Button,会产生一个ActionEvent 事件),事件会被相应的监听者捕获并执行相关的操作(例如打开一个新窗口),从而达到与用户交互的目的,这个过程就是Java的事件处理机制,如图2 所示。
事件处理机制中包含了三个重要的概念,分别是事件源、事件和监听器。事件源是产生事件的场所,通常是一些具体的组件,这些组件扮演了Observer 模式中的被观察者角色。事件是事件源产生的具体对象,充当连接事件源和监听器的纽带作用。Java 中定义了许多不同的事件类以描述GUI 程序中可能产生的所有事件,这些事件类都继承自java.awt.AWTEvent,分为两大类:低级事件和高级事件[5]。低级事件通常基于组件和容器对象,例如鼠标在一个组件上执行单击、拖动等动作。高级事件基于语义的,可以不和特定的动作相关联而依赖于事件源的类型。监听器是当事件源产生事件后对其进行接收和处理的对象,每一种事件都对应专门的监听器[6]。通过监听器使事件的触发地点和实际处理地点分离,降低了系统内对象的耦合性。监听器包括监听接口和监听接口实现类两部分。java 根据不同的事件类型定义了不同的监听接口,监听接口中定义了若干个针对同一事件所触发的不同动作的处理方法。监听接口扮演了Observer 模式中的观察者接口角色,监听接口的实现类扮演了Observer模式中的具体观察者角色,事件源需要调用注册方法来指定监听接口实现类的对象作为它的观察者。
图2 Java 事件处理机制模型图
4 事件处理机制的三种实现方式
以我院教职工信息管理系统为例,详细描述Observer 模式在Java 事件处理机制中的应用,给出了三种事件处理方案并比较了它们的优缺点。以点击系统主窗体所含菜单的某个菜单项,弹出对应的新窗体为情景。菜单项为事件源,当它被点击后会产生一个ActionEvent 事件(这是一个高级事件),从Observer 模式的角度去理解相当于被观察者的状态发生了改变,它会调用notify()方法通知所有注册的事件监听器,并将事件的引用传递给监听器。ActionEvent 事件对应的监听接口为ActionListener。在下面的描述中用被观察者称谓代替事件源,观察者称谓代替监听器。
4.1 被观察者与观察者在同一个类中
菜单项必须依附于菜单,菜单依附于菜单栏,菜单栏添加在一个窗体中。因此事件源是窗体的属性,而一般情况下自定义的窗体类都是从Frame 或JFrame 继承而来,所以窗体类要实现观察者接口,它的对象作为具体观察者。核心代码如下:
此种方式的优点在于不用单独生成具体观察者对象,由于被观察者对象所属类实现了观察者接口,因此被观察者对象在注册观察者对象的方法中传递this 就可以了。缺点是很可能存在多个同种类型的被观察者对象,它们会产生相同类型的事件,而且不同类型的被观察者也有可能产生相同的事件(例如Button 和MenuItem 都会产生ActionEvent 事件),所以观察者对象在对事件进行操作的代码中增加了额外的判断被观察者对象的逻辑。
4.2 观察者是被观察者所属类的内部类
此种方式的特点是观察者与被观察者在同一个类中,但观察者类成了被观察者所在类的内部类,核心代码如下:
与第一种方式相比,此种方式的优点是被观察者对象所属的类不再承担观察者的任务,实现了页面显示逻辑与监听逻辑相分离;监听逻辑中不需要判断被观察者对象了。缺点是需要为不同的被观察者重新定义相对应的观察者类,可能会出现较多的内部类,被观察者添加监听器时创建观察者对象。
4.3 观察者是被观察者所属类的匿名内部类
此种方式的特点是观察者对象所属类是被观察者对象所属类的内部类,只不过这个内部类没有具体名称,所以称为匿名内部类。匿名内部类通常需要继承一个父类或实现一个接口,核心代码如下:
与第二种方式相比,此种方式的优点是省去了观察者作为内部类的命名问题,在被观察者注册监听器的方法中完成匿名内部类的定义与对象的创建。由于没有引用的存在,这个匿名内部类对象在完成相应的观察者功能后会被Java的垃圾回收机制直接回收,节省了内存空间。而且这种书写方式使得代码看上去简洁清楚。缺点是匿名内部类的定义与创建对象与普通类还是有明显区别的,初学者不太容易理解和掌握。
5 结束语
设计模式是设计级的软件重用方法,通过面向抽象接口编程的方法来降低类间耦合,达到建造具有良好扩展性、健壮性的系统[7]。分析了Observer模式的基本原理并将其应用到Java 事件处理机制的实现中,根据被观察者与观察者在同一个类中、观察者是被观察者所在类的内部类、观察者是被观察者所在类的匿名内部类这三种情况,给出了具体的代码实现并分析了三者的优缺点。由于监听器的任务很单一,就是对事件源产生的事件进行处理,所以从命名、节省内存、对象的作用域等几个方面考虑,采用匿名内部类实现事件处理机制最为合适,优先推荐使用第三种方式。
[1]Erich Gamma,Richard Helm,Ralph Johnson,John Vlissides.Design Patterns:Elements of Reusable Objected-Oriented Software[M].Reading,MA:Addison-Wesley,1994:2-20.
[2]葛萌,杨卫忠,欧阳宏基.工厂设计模式在Java RMI 中的应用研究[J].计算机与数字工程,2013,41(2):307-307.
[3]耿祥义,张跃平.Java 设计模式[M].北京:清华大学出版社,2009:34-35.
[4]肖力涛,亓常松.基于MVC的Observer 开发模式的扩展及应用[J].计算机与现代化,2012(5):204-205.
[5]邢素萍,王健南.谈Java 技术中的事件处理与应用[J].微型电脑应用,2011,27(12):63-63.
[6]杜春涛.Java 6 基础教程[M].北京:清华大学出版社,2011:288-289.
[7]宋淼,袁兆山,陈刚,刘奎.Java 事件处理机制中设计模式的分析[J].合肥工业大学学报(自然科学版),2004,27(11):1386-1386.