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

来源:http://www.cnblogs.com/luminji/archive/2017/06/05/6944526.html
-Advertisement-
Play Games

“事件”這個詞已經被濫用了。正因為“事件”的被濫用,很多人在用到事件的時候不求甚解,依樣畫葫蘆,導致學習工作了很多年,還是不清楚什麼是事件處理器、什麼是事件持有者。所以,如果你對於Event這個詞還是心存恐懼,那麼本文正是你需要的。讓我們從易到難,從具體到抽象,一步一步來解釋java事件處理機制。 ... ...


“事件”這個詞已經被濫用了。正因為“事件”的被濫用,很多人在用到事件的時候不求甚解,依樣畫葫蘆,導致學習工作了很多年,還是不清楚什麼是事件處理器、什麼是事件持有者。所以,如果你對於Event這個詞還是心存恐懼,那麼本文正是你需要的。讓我們從易到難,從具體到抽象,一步一步來解釋java事件處理機制。

一:觀察者模式

要瞭解事件和監聽,我們首先來必須要瞭解觀察者模式。

什麼是觀察者模式,我們先來看一個熟悉的場景:

1:教師佈置作業,通知學生;

2:學生觀察到老師佈置了作業,開始做作業;

首先我明確下,我歷來是十分反對以阿貓阿狗、老師學生這樣的場景來闡述代碼問題的,所以該主題的最後肯定會回到實際代碼中來。言歸正傳,在這個場景中,學生就是觀察者,教師就是被觀察者,但是大家一定要註意:

教師作為被觀察者,實際上是掌握著主動的,且看上文中我加粗的“通知”二字,因為這貌似看上去簡簡單單的通知,事實上卻要做很多事情(寫很多代碼)。

好了,我們先來實現上面的場景:

image

代碼:

觀察者,學生

package com.zuikc.events;

import java.util.Observable;

public class Student implements java.util.Observer {

    private String name;
    public Student(String name){
        this.name = name;
    }
    @Override
    public void update(Observable o, Object arg) {
        Teacher teacher = (Teacher) o;
        System.out.printf("學生%s觀察到(實際是被通知)%s佈置了作業《%s》 \n", this.name, teacher.getName(), arg);
    }

}

被觀察者,教師

package com.zuikc.events;

import java.util.*;

public class Teacher extends java.util.Observable {

    private String name;
    private List<String> homeworks;

    public String getName() {
        return this.name;
    }

    public Teacher(String name) {
        this.name = name;
        homeworks = new ArrayList<String>();
    }

    public void setHomework(String homework) {
        System.out.printf("%s佈置了作業%s \n", this.name, homework);
        homeworks.add(homework);
        setChanged();
        notifyObservers(homework);

    }
}

客戶端:

package com.zuikc.events;

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

}

很多初學者有個錯覺,考慮“觀察”這個動作是主動的,所以就認為在代碼實現上,Reader是主動調用自己的update,但是很遺憾,當然不是,在代碼實現上,update是“被觀察者”Teacher主動調用的。有人說,我只在Teacher中看到了

        setChanged();
        notifyObservers(homework);

沒有看到它調用update呀,那麼請查看它的父類Observable,在notifyObservers方法中有,

for (int i = arrLocal.length-1; i>=0; i--)
    ((Observer)arrLocal[i]).update(this, arg);

看看結果吧:

image

註意,以上代碼中,我直接使用了java.util包中的類Observable和介面Observer,我們當然也可以自己寫這兩個東東。

 

二:基礎版觀察者模式

初學者對於一上來就使用java.util中的api不習慣,可能覺得看不到摸不著,那我們就自己來寫一個基礎版的觀察者模式,大家感受下,在寫的過程中,一定要對照上節中的UML圖和代碼,然後心中默念:它們沒有區別,它們沒有區別!

上圖:

image

上代碼:

觀察者,介面

package com.zuikc;

public interface Observer {

    void update(Observable o);
}

具體觀察者,我寫了兩個:

class ConcreteObserver1 implements Observer {

    public void update(Observable o) {
        System.out.println("觀察者1觀察到" + o.getClass().getSimpleName() + "發生變化");
        System.out.println("觀察者1做出響應");
    }
}

class ConcreteObserver2 implements Observer {

    public void update(Observable o) {
        System.out.println("觀察者2觀察到" + o.getClass().getSimpleName() + "發生變化");
        System.out.println("觀察者2做出響應");
    }
}

觀察者

package com.zuikc;

import java.util.ArrayList;
import java.util.List;

public class Observable {

    List<Observer> observers = new ArrayList<Observer>();

    public void addObserver(Observer o) {
        observers.add(o);
    }

    public void doSomething() {
        System.out.println("我是被觀察者,我發生變化了");
        // 主動去通知所有的觀察者
        notifyObservers();
    }

    public void notifyObservers() {
        for (Observer observer : observers) {
            observer.update(this);
        }
    }
}

客戶端:

package com.zuikc;

public class Client {

    public static void main(String[] args) {
        Observable observable = new Observable();
        observable.addObserver(new ConcreteObserver1());
        observable.addObserver(new ConcreteObserver2());
        observable.doSomething();
    }

}

大家可以自己運行下代碼,看看發生了什麼。

在上面代碼中,我們自己了一個介面,一個被觀察者類,雖然簡單了一點,但是卻達到了演示的效果。當然,我們也可以原封不動的把JDK中的源碼拷貝出來作為我們這兩個文件的代碼。

如果對於上面的代碼已經相當之熟悉,你還可以研究下update方法,在第一小節中我們是帶了arg參數的,但是這個基礎版本中沒帶。無所謂,你想帶就帶,不想帶就不想帶,代碼即自由,只要你實現了功能。

 

三:觀察者模式的用意

基礎版的觀察者模式畢竟太簡單,在我們第一節中的代碼中,我們可以總結出:

1:教師類和學生類無關,他只依賴觀察者介面,如果有一天,他的作業不僅僅佈置給學生,作為優秀講師,還要發送給全校的老師作為參考,那麼只要老師這個類也實現觀察者介面,我們同樣可以將老師添加到這個教師的觀察者列表中;

2:觀察者模式分離了觀察者和被觀察者自身的責任,讓類各自維護自己的功能,提高了系統的可重用性;

3:觀察看上去是一個主動的行為,但是其實觀察者不是主動調用自己的業務代碼的,相反,是被觀察者調用的。所以,觀察者模式還有另一個名字,叫發佈-訂閱模式,我認為,後者更貼切;

 

觀察者模式還有另外一種形態,就是事件驅動模型,這兩種方式在實現機制上是非常接近的,在理解了觀察者模式的基礎上,理解事件驅動,就非常簡單了。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、thinkphp配置類型有哪些? 1.在thinkphp中,有6種配置。即慣例配置,應用配置、擴展配置、模塊配置、場景配置、動態配置。 2.慣例配置就是系統預設的配置。 3.應用配置就是我們自己開發創建的應用單獨的配置。 4.擴展配置主要是實現我們項目在開發過程中有一些擴展程式用到的配置。 5. ...
  • 顯式鎖 一、Lock與ReentrantLock Lock提供了一種無條件的、可輪詢的、定時的以及可中斷的鎖獲取操作,所有的加鎖和解鎖方法都是顯式的 ReentrantLock實現了Lock:並提供了和synchronized相同的記憶體語義;同時提供了可重入的加鎖語義 1.基本語義: 2.輪詢鎖與定 ...
  • Java運算符 運算符:是一種用於對數據進行運算,賦值,比較的特殊符號。 Java語言中運算符總共分為以下幾大類: 算術運算符:+ - * / ++ -- 示例如下代碼: 賦值運算符 首先要註意賦值運算符在Java中的優先順序是最低的,即在有其它運算符存在的運算式中永遠最後執行賦值運算符 示例代碼如下 ...
  • Python基礎 類變數和實例變數 寫在前面 如非特別說明,下文均基於Python3 大綱: 1. 類變數和實例變數 在 "Python Tutorial" 中對於類變數和實例變數是這樣描述的: Generally speaking, instance variables are for data ...
  • ​對於Java程式猿學習的建議 ​對於Java程式猿學習的建議 這一部分其實也算是今天的重點,這一部分用來回答很多群里的朋友所問過的問題,那就是LZ你是如何學習Java的,能不能給點建議? 今天LZ是打算來點乾貨,因此咱們就不說一些學習方法和技巧了,直接來談每個階段要學習的內容甚至是一些書籍。這一部 ...
  • 要深入學習註解,我們就必須能定義自己的註解,並使用註解,在定義自己的註解之前,我們就必須要瞭解Java為我們提供的元註解和相關定義註解的語法。 ...
  • 學習Java的同學註意了!!!學習過程中遇到什麼問題或者想獲取學習資源的話,歡迎加入Java學習交流群:159610322 我們一起學Java! 首先我們來講講:重載(Overloading) (1) 方法重載是讓類以統一的方式處理不同類型數據的一種手段。多個同名函數同時存在,具有不同的參數個數/類 ...
  • 併發程式測試 一、正確性測試 如:對一個自定義緩存的測試 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...