第一节中的示例过于简单《从零开始理解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)的更多相关文章

  1. 从零开始理解JAVA事件处理机制(3)

    我们连续写了两小节的教师-学生的例子,必然觉得无聊死了,这样的例子我们就是玩上100遍,还是不知道该怎么写真实的代码.那从本节开始,我们开始往真实代码上面去靠拢. 事件最容易理解的例子是鼠标事件:我们 ...

  2. 从零开始理解JAVA事件处理机制(1)

    “事件”这个词已经被滥用了.正因为“事件”的被滥用,很多人在用到事件的时候不求甚解,依样画葫芦,导致学习工作了很多年,还是不清楚什么是事件处理器.什么是事件持有者.所以,如果你对于Event这个词还是 ...

  3. Java事件处理机制(深入理解)

    本文是关于Java事件处理机制的梳理,以及有重点的介绍一些注意点,至于基础的概念啥的不多赘述. 一.Java事件处理机制初步介绍(看图理解) 根据下图,结合生活实际,可以得知监护人可以有多个,坏人对小 ...

  4. java事件处理机制

    java中的事件机制的参与者有3种角色:   1.event object:就是事件产生时具体的"事件",用于listener的相应的方法之中,作为参数,一般存在与listerne ...

  5. 理解Java类加载机制(译文)

    理解java类加载机制 你想写类加载器?或者你遇到了ClassCastException异常,或者你遇到了奇怪的LinkageError状态约束异常.应该仔细看看java类的加载处理了. 什么是类加载 ...

  6. Java基础 -- 深入理解Java异常机制

    异常指不期而至的各种状况,如:文件找不到.网络连接失败.非法参数等.异常是一个事件,它发生在程序运行期间,干扰了正常的指令流程.Java通 过API中Throwable类的众多子类描述各种不同的异常. ...

  7. [转]Java事件处理机制- 事件监听器的四种实现方式

    原文来自http://stefan321.iteye.com/blog/345221 自身类作为事件监听器 外部类作为事件监听器 匿名内部类作为事件监听器 内部类作为事件监听器 自身类作为事件监听器: ...

  8. 【转】深入理解java异常处理机制

    深入理解java异常处理机制 ; int c; for (int i = 2; i >= -2; i--) { c = b / i; System.out.println("i=&qu ...

  9. 转:一个经典例子让你彻彻底底理解java回调机制

    一个经典例子让你彻彻底底理解java回调机制 转帖请注明本文出自xiaanming的博客(http://blog.csdn.net/xiaanming/article/details/17483273 ...

随机推荐

  1. 为部署ASP.NET Core准备:使用Hyper-V安装Ubuntu Server 16.10

    概述 Hyper-V是微软的一款虚拟化产品,和VMWare一样采用的hypervisor技术.它已经被内嵌到Win10系统内,我们只需要进行简单的安装即可.但是前提是要确保你的机器已经启用虚拟化,可以 ...

  2. 免费给自己的网站加 HTTPS

    简介 本文是通过 Let's Encrypt 提供的免费证书服务,实现让自己的网站加上 HTTPS.我的网站 -- hellogithub,就是通过这种方式实现的 HTTPS,效果如下: Let's ...

  3. CSS也需要重构

    最初接触到的CSS面向对象,是项目里的CSS超过8千行,缺乏约束和管理,在近期或不远的将来,有迫切的要求需要重构.CSS面向对象和模块化. CSS代码遇到的问题: 重用性差,看着一个CSS的名称,很难 ...

  4. redis 字典

    redis 字典 前言 借鉴了 黄健宏 的 <<Redis 设计与实现>> 一书, 对 redis 源码进行学习 欢迎大家给予意见, 互相沟通学习 概述 字典是一种用于存储键值 ...

  5. Linux防火墙配置—SNAT2

    1.实验目标 以实验"Linux防火墙配置-SNAT1"为基础,为网关增加外网IP地址,为eth1创建虚拟接口,使外网测试主机在Wireshark中捕获到的地址为eth1虚拟接口的 ...

  6. 二、AspNet Core添加EF的基本方法(简略版):

    _/\__ ---==/ \\ |. \|\ | ) \\\ \_/ | //|\\ / \\\/\\ 1.在Project.json的dependencies选项中添加以下引用: "Mic ...

  7. NodeJS 事件循环

    Node.js 是单进程单线程应用程序,但是通过事件和回调支持并发,所以性能非常高. Node.js 的每一个 API 都是异步的,并作为一个独立线程运行,使用异步函数调用,并处理并发. Node.j ...

  8. Ionic2 + Angular4 + JSSDK开发中的若干问题汇总

    前景 目前微信公众号程序开发已经相当火热,客户要求自己的系统有一个公众号,已经是一个很常见的需要. 使用公众号可以很方便的便于项目干系人查看信息和进行互动,还可以很方便录入一些电脑端不便于录入的数据, ...

  9. mysql varchar类型转换int类型找出最大值

    (1) 不严谨的,最简单的 select MAX(字段名 + 0) from 表名; (2) 使用函数实现 select MAX(cast(字段名 as SIGNED INTEGER)) from 表 ...

  10. JAVA 基础之Integer

    jdk1.5后增加了自动拆箱和自动装箱特性.java的八种 byte,short,int,long,float,double,char,boolean基本类型和各自对应的包装类型的相互转化. 装箱指的 ...