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

來源:互聯網
上載者:User

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

第一節中的樣本過於簡單《從零開始理解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:在事件模型中,每個監聽者(觀察者)都需要實現一個自己的介面。沒錯,看看我們的滑鼠事件,分表就有單擊、雙擊、移動等等的事件,這分別就是增加了代碼的靈活性;

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

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.