天天看點

從零開始了解JAVA事件處理機制(2)

一:事件驅動模型初窺

我們要說事件驅動模型是觀察者模式的更新版本,那我們就要說說其中的對應關系:

觀察者對應監聽器(學生) 被觀察者對應事件源(教師)

事件源産生事件,事件帶有事件源,監聽器監聽事件。愛鑽牛角尖的朋友可能會說,我擦,什麼叫産生事件,監聽事件,事件事件到底什麼?

莫慌,如果我們用代碼來說事,事件它就是個類,事件源也是個類。這裡面一共牽扯到四個類,事件源(即教師、即被觀察者)、事件(是一個類,見下文,一般我們以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方法,傳回的就是事件源(即教師、即被觀察者),

* <p> * 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. * 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;     }      * @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的設計者們進行了最進階的抽象,就是讓上層類隻是代表了:我是一個事件(含有事件源),或,我是一個監聽者!

二:老師布置作業的事件驅動模型版本

老規矩,讓我們先給出類圖:

從零開始了解JAVA事件處理機制(2)

然後,代碼實作之:

觀察者接口(學生)。由于在事件驅動模型中,隻有一個沒有任何方法的接口,EventListener,是以,我們可以先實作一個自己的接口。為了跟上一篇的代碼保持一緻,在該接口中我們聲明的方法的名字也叫update。注意,我們當然也可以不取這個名字,甚至還可以增加其它的方法聲明,全看我們的業務需要。

package com.zuikc.events; import java.util.Observable; public interface HomeworkListener extends java.util.EventListener {     public void update(HomeworkEventObject o, Object arg);

繼而實作觀察者類(學生),如下:

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);

對比一下上篇,有變動沒?

繼而實作事件子類,如下:

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方法也可行,但是重新在子類封裝一下,可讀性更強一點。

然後呢,然後就是我們的教師類,教師類就是事件源,如下:

import java.util.*; public class Teacher {     private List<String> homeworks;     /*       * 教師類要維護一個自己監聽器(學生)的清單,為什麼?      * 在觀察者模式中,教師是被觀察者,繼承自java.util.Observable,Observable中含了這個清單      * 現在我們沒有這個清單了,是以要自己建立一個     private Set<HomeworkListener> homeworkListenerList;     public String getName() {         return this.name;     public Teacher(String 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);

這一點問題也沒有,我們繼續來看用戶端代碼吧:

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("事件機制第二天作業");

結果如下:

從零開始了解JAVA事件處理機制(2)

從用戶端的角度來說,我們幾乎完全沒有更改任何地方,跟觀察者模式的用戶端代碼一模一樣,但是内部的實作機制上,我們卻使用了事件機制。

現在我們來總結下,觀察者模式和事件驅動模型的幾個不同點:

1:事件源不再繼承任何模式或者模型本身的父類,徹底将業務代碼解耦出來;

2:在事件模型中,每個監聽者(觀察者)都需要實作一個自己的接口。沒錯,看看我們的滑鼠事件,分表就有單擊、輕按兩下、移動等等的事件,這分别就是增加了代碼的靈活性;

不管怎麼說,我們用一堆小白代碼實作了一個事件驅動模型的樣例,雖然沒什麼實際用處,但也解釋了原理,接下來的一節,我們就要看看那些稍微複雜且看上去很有用的代碼了!

本文轉自最課程陸敏技部落格園部落格,原文連結:http://www.cnblogs.com/luminji/p/6947096.html,如需轉載請自行聯系原作者