java 事件处理机制_从零开始理解JAVA事件处理机制(2)

第一节中的示例过于简单《从零开始理解JAVA事件处理机制(1)》,简单到让大家觉得这样的代码简直毫无用处。但是没办法,我们要继续写这毫无用处的代码,然后引出下一阶段真正有益的代码。

一:事件驱动模型初窥

我们要说事件驱动模型是观察者模式的升级版本,那我们就要说说其中的对应关系:

观察者对应监听器(学生)

被观察者对应事件源(教师)

事件源产生事件,事件带有事件源,监听器监听事件。爱钻牛角尖的朋友可能会说,我擦,什么叫产生事件,监听事件,事件事件到底什么?

莫慌,如果我们用代码来说事,事件它就是个类,事件源也是个类。这里面一共牵扯到四个类,事件源(即教师、即被观察者)、事件(是一个类,见下文,一般我们以Event或者EventObject命名结尾)、监听器接口、具体的监听器(即学生、即观察者)。

就像我们上一篇文章中的第一节提到的一样,JDK中当然有现成的事件模型类,我们不妨来一个一个的查看一下吧。

首先看监听器(即学生、即观察者,大家不要嫌我烦,不停滴括号提醒,这是为了不停滴给大家加深印象),

package java.util;

/**

* A tagging interface that all event listener interfaces must extend.

* @since JDK1.1

*/

public interface EventListener {

}

简单到不能再简单了,对吧,甚至连一个声明的方法都没有,那它存在的意义在哪?还记得面向对象中的上溯造型吗,所以它的意义就在于告诉所有的调用者,我是一个监听器。

再来看看事件,即Event或EventObject结尾的那个类,里面含有getSource方法,返回的就是事件源(即教师、即被观察者),

package java.util;

/**

*

* The root class from which all event state objects shall be derived.

*

* All Events are constructed with a reference to the object, the "source",

* that is logically deemed to be the object upon which the Event in question

* initially occurred upon.

*

* @since JDK1.1

*/

public class EventObject implements java.io.Serializable {

private static final long serialVersionUID = 5516075349620653480L;

/**

* The object on which the Event initially occurred.

*/

protected transient Object  source;

/**

* Constructs a prototypical Event.

*

* @param    source    The object on which the Event initially occurred.

* @exception  IllegalArgumentException  if source is null.

*/

public EventObject(Object source) {

if (source == null)

throw new IllegalArgumentException("null source");

this.source = source;

}

/**

* The object on which the Event initially occurred.

*

* @return   The object on which the Event initially occurred.

*/

public Object getSource() {

return source;

}

/**

* Returns a String representation of this EventObject.

*

* @return  A a String representation of this EventObject.

*/

public String toString() {

return getClass().getName() + "[source=" + source + "]";

}

}

这个类也很简单,如果说观察者模式中的上层类和结果还带了不少逻辑不少方法的话,那么事件驱动模型中的上层类和接口简直看不到任何东西。没错,

事件驱动模型中,JDK的设计者们进行了最高级的抽象,就是让上层类只是代表了:我是一个事件(含有事件源),或,我是一个监听者!

二:老师布置作业的事件驱动模型版本

老规矩,让我们先给出类图:

4b342992c64728475d9d1f2d8150d73e.png

然后,代码实现之:

观察者接口(学生)。由于在事件驱动模型中,只有一个没有任何方法的接口,EventListener,所以,我们可以先实现一个自己的接口。为了跟上一篇的代码保持一致,在该接口中我们声明的方法的名字也叫update。注意,我们当然也可以不取这个名字,甚至还可以增加其它的方法声明,全看我们的业务需要。

package com.zuikc.events;

import java.util.Observable;

public interface HomeworkListener extends java.util.EventListener {

public void update(HomeworkEventObject o, Object arg);

}

继而实现观察者类(学生),如下:

package com.zuikc.events;

public class Student implements HomeworkListener{

private String name;

public Student(String name){

this.name = name;

}

@Override

public void update(HomeworkEventObject o, Object arg) {

Teacher teacher = o.getTeacher();

System.out.printf("学生%s观察到(实际是被通知)%s布置了作业《%s》 \n", this.name, teacher.getName(), arg);

}

}

对比一下上篇,有变动没?

继而实现事件子类,如下:

package com.zuikc.events;

public class HomeworkEventObject extends java.util.EventObject {

public HomeworkEventObject(Object source) {

super(source);

}

public HomeworkEventObject(Teacher teacher) {

super(teacher);

}

public Teacher getTeacher(){

return (Teacher) super.getSource();

}

}

在这个类中,指的关注的就是这个getTeacher方法,它封装了父类EventObject的getSource方法,得到了事件源。理论上,我们使用父类的getSource方法也可行,但是重新在子类封装一下,可读性更强一点。

然后呢,然后就是我们的教师类,教师类就是事件源,如下:

package com.zuikc.events;

import java.util.*;

public class Teacher {

private String name;

private List homeworks;

/*

* 教师类要维护一个自己监听器(学生)的列表,为什么?

* 在观察者模式中,教师是被观察者,继承自java.util.Observable,Observable中含了这个列表

* 现在我们没有这个列表了,所以要自己创建一个

*/

private Set homeworkListenerList;

public String getName() {

return this.name;

}

public Teacher(String name) {

this.name = name;

this.homeworks = new ArrayList();

this.homeworkListenerList  = new HashSet();

}

public void setHomework(String homework) {

System.out.printf("%s布置了作业%s \n", this.name, homework);

homeworks.add(homework);

HomeworkEventObject event = new HomeworkEventObject(this);

/*

* 在观察者模式中,我们直接调用Observable的notifyObservers来通知被观察者

* 现在我们只能自己通知了~~

*/

for (HomeworkListener listener : homeworkListenerList) {

listener.update(event, homework);

}

}

public void addObserver(HomeworkListener homeworkListener){

homeworkListenerList.add(homeworkListener);

}

}

这个类稍微长了那么一点点,有几个地方值得注意:

第一处地方,Teacher没有父类了,Teacher作为事件中的事件源Source被封装到HomeworkEventObject中了。这没有什么不好的,业务对象和框架代码隔离开来,解耦的非常好,但是正因为如此,我们需要在Teacher中自己维护一个Student的列表,于是,我们看到了homeworkListenerList这个变量。

第二处,在观察者模式中,我们直接调用Observable的notifyObservers来通知被观察者,现在我们只能靠自己了,于是我们看到了这段代码,

for (HomeworkListener listener : homeworkListenerList) {

listener.update(event, homework);

}

这一点问题也没有,我们继续来看客户端代码吧:

package com.zuikc.events;

import java.util.EventListener;

public class Client {

public static void main(String[] args) {

Student student1= new Student("张三");

Student student2 = new Student("李四");

Teacher teacher1 = new Teacher("zuikc");

teacher1.addObserver(student1);

teacher1.addObserver(student2);

teacher1.setHomework("事件机制第二天作业");

}

}

结果如下:

397c3566219f6d5de10c1af1a5587719.png

从客户端的角度来说,我们几乎完全没有更改任何地方,跟观察者模式的客户端代码一模一样,但是内部的实现机制上,我们却使用了事件机制。

现在我们来总结下,观察者模式和事件驱动模型的几个不同点:

1:事件源不再继承任何模式或者模型本身的父类,彻底将业务代码解耦出来;

2:在事件模型中,每个监听者(观察者)都需要实现一个自己的接口。没错,看看我们的鼠标事件,分表就有单击、双击、移动等等的事件,这分别就是增加了代码的灵活性;

不管怎么说,我们用一堆小白代码实现了一个事件驱动模型的样例,虽然没什么实际用处,但也解释了原理,接下来的一节,我们就要看看那些稍微复杂且看上去很有用的代码了!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值