从零开始理解JAVA事件处理机制(2)
第一节中的示例过于简单《从零开始理解JAVA事件处理机制(1)》,简单到让大家觉得这样的代码简直毫无用处。但是没办法,我们要继续写这毫无用处的代码,然后引出下一阶段真正有益的代码。
一:事件驱动模型初窥
我们要说事件驱动模型是观察者模式的升级版本,那我们就要说说其中的对应关系:
观察者对应监听器(学生)
被观察者对应事件源(教师)
事件源产生事件,监听器监听事件。爱钻牛角尖的朋友可能会说,我擦,什么叫产生事件,监听事件,事件事件到底什么?
莫慌,如果我们用代码来说事,事件源它就是个类,事件就是事件源中的那几行业务代码。这里面一共牵扯到四个类,事件源(即教师、即被观察者)、事件、监听器接口、具体的监听器(即学生、即观察者)。
就像我们上一篇文章中的第一节提到的一样,JDK中当然有现成的事件模型类,我们不妨来一个一个的查看一下吧。
首先看监听器(即学生、即观察者,大家不要嫌我烦,不停滴括号提醒,这是为了不停滴给大家加深印象),
package java.util;
/**
* A tagging interface that all event listener interfaces must extend.
* @since JDK1.1
*/
public interface EventListener {
}
简单到不能再简单了,对吧,甚至连一个声明的方法都没有,那它存在的意义在哪?还记得面向对象中的上溯造型吗,所以它的意义就在于告诉所有的调用者,我是一个监听器。
再来看看事件源(即教师、即被观察者),
package java.util;
/**
* <p>
* The root class from which all event state objects shall be derived.
* <p>
* 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的设计者们进行了最高级的抽象,就是让上层类只是代表了:我是一个事件源,或,我是一个监听者!
二:老师布置作业的事件驱动模型版本
老规矩,让我们先给出类图:

然后,代码实现之:
观察者接口(学生)。由于在事件驱动模型中,只有一个没有任何方法的接口,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<String> homeworks;
/*
* 教师类要维护一个自己监听器(学生)的列表,为什么?
* 在观察者模式中,教师是被观察者,继承自java.util.Observable,Observable中含了这个列表
* 现在我们没有这个列表了,所以要自己创建一个
*/
private Set<HomeworkListener> homeworkListenerList;public String getName() {
return this.name;
}public Teacher(String name) {
this.name = name;
this.homeworks = new ArrayList<String>();
this.homeworkListenerList = new HashSet<HomeworkListener>();
}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("事件机制第二天作业");
}}
结果如下:

从客户端的角度来说,我们几乎完全没有更改任何地方,跟观察者模式的客户端代码一模一样,但是内部的实现机制上,我们却使用了事件机制。
现在我们来总结下,观察者模式和事件驱动模型的几个不同点:
1:事件源不再继承任何模式或者模型本身的父类,彻底将业务代码解耦出来;
2:在事件模型中,每个监听者(观察者)都需要实现一个自己的接口。没错,看看我们的鼠标事件,分表就有单击、双击、移动等等的事件,这分别就是增加了代码的灵活性;
不管怎么说,我们用一堆小白代码实现了一个事件驱动模型的样例,虽然没什么实际用处,但也解释了原理,接下来的一节,我们就要看看那些稍微复杂且看上去很有用的代码了!
从零开始理解JAVA事件处理机制(2)的更多相关文章
- 从零开始理解JAVA事件处理机制(3)
我们连续写了两小节的教师-学生的例子,必然觉得无聊死了,这样的例子我们就是玩上100遍,还是不知道该怎么写真实的代码.那从本节开始,我们开始往真实代码上面去靠拢. 事件最容易理解的例子是鼠标事件:我们 ...
- 从零开始理解JAVA事件处理机制(1)
“事件”这个词已经被滥用了.正因为“事件”的被滥用,很多人在用到事件的时候不求甚解,依样画葫芦,导致学习工作了很多年,还是不清楚什么是事件处理器.什么是事件持有者.所以,如果你对于Event这个词还是 ...
- Java事件处理机制(深入理解)
本文是关于Java事件处理机制的梳理,以及有重点的介绍一些注意点,至于基础的概念啥的不多赘述. 一.Java事件处理机制初步介绍(看图理解) 根据下图,结合生活实际,可以得知监护人可以有多个,坏人对小 ...
- java事件处理机制
java中的事件机制的参与者有3种角色: 1.event object:就是事件产生时具体的"事件",用于listener的相应的方法之中,作为参数,一般存在与listerne ...
- 理解Java类加载机制(译文)
理解java类加载机制 你想写类加载器?或者你遇到了ClassCastException异常,或者你遇到了奇怪的LinkageError状态约束异常.应该仔细看看java类的加载处理了. 什么是类加载 ...
- Java基础 -- 深入理解Java异常机制
异常指不期而至的各种状况,如:文件找不到.网络连接失败.非法参数等.异常是一个事件,它发生在程序运行期间,干扰了正常的指令流程.Java通 过API中Throwable类的众多子类描述各种不同的异常. ...
- [转]Java事件处理机制- 事件监听器的四种实现方式
原文来自http://stefan321.iteye.com/blog/345221 自身类作为事件监听器 外部类作为事件监听器 匿名内部类作为事件监听器 内部类作为事件监听器 自身类作为事件监听器: ...
- 【转】深入理解java异常处理机制
深入理解java异常处理机制 ; int c; for (int i = 2; i >= -2; i--) { c = b / i; System.out.println("i=&qu ...
- 转:一个经典例子让你彻彻底底理解java回调机制
一个经典例子让你彻彻底底理解java回调机制 转帖请注明本文出自xiaanming的博客(http://blog.csdn.net/xiaanming/article/details/17483273 ...
随机推荐
- mpush 服务器环境配置安装 CentOS 7 and Windows
github-doc https://github.com/mywiki/mpush-doc/blob/master/SUMMARY.md Introduction 1.服务器环境 2.安装Redis ...
- OutOfMemoryError内存不足
java.lang.OutOfMemoryError内存不足错误.当可用内存不足以让Java虚拟机分配给一个对象时抛出该错误. 造成此错误的原因有一下几个: 1.内存中加载的数据量过于庞大,如一次从数 ...
- mpu6050参数获取
MPU6050其实就是一个 I2C 器件,里面有很多寄存器(但是我们用到的只有几个),我们通过读写寄存器来操作这个芯片.所以首要问题就是 STM32 和 MPU6050 的 I2C 通信.1.配置 S ...
- 开启新模式WinForm
从今天开始咱们正式进入WinForm开发模式 首先很官方的介绍下什么是winform:客户端应用程序:C/S 这就是winform 有什么特别特别重要的特点呢:可以操作用户电脑上的文件 举个简单的例子 ...
- 用PetaPoco为ASP.NET已有数据库建模
序:最近一直在抓紧重构公司的网站,没有很多时间去写博客,积累了很多的问题,几乎是一天一个,折腾死了,尤其是在模型方面几经周折. 以前,多半从事PHP开发,很少接触到模型(thinkphp中模型),但是 ...
- Hadoop化繁为简-从安装Linux到搭建集群环境
简介与环境准备 hadoop的核心是分布式文件系统HDFS以及批处理计算MapReduce.近年,随着大数据.云计算.物联网的兴起,也极大的吸引了我的兴趣,看了网上很多文章,感觉还是云里雾里,很多不必 ...
- Linux的正则表达式grep,egrep
一.概念 正则表达式是对字符串操作的一种逻辑公式,用事先定义好的一组特殊字符,组成一个"规则字符集合",根据用户指定的文本模式对目标文件进行逐行搜索匹配,显示能被模式匹配到的结果. ...
- 根据优先关系矩阵使用逐次加一发构造优先函数(Java)
若已知运算符之间的优先关系,可按如下步骤构造优先函数: 1.对每个运算符a(包括#在内)令f(a)=g(a)=1 2.如果a⋗b且f(a)<=g(b)令f(a)=g(b)+1 3.如果a⋖b且f ...
- LeetCode 题目总结/分类
LeetCode 题目总结/分类 利用堆栈: http://oj.leetcode.com/problems/evaluate-reverse-polish-notation/ http://oj.l ...
- 调停者(Mediator)模式
调停者模式是对象的行为模式.调停者模式包装了一系列对象相互作用的方式,使得这些对象不必相互明显引用.从而使它们可以较松散地耦合.当这些对象中的某些对象之间的相互作用发生改变时,不会立即影响到其他的一些 ...