從零開始理解JAVA事件處理機制(1)
“事件”這個詞已經被濫用了。正因為“事件”的被濫用,很多人在用到事件的時候不求甚解,依樣畫葫蘆,導致學習工作了很多年,還是不清楚什麼是
事件處理器
、什麼是事件持有者。所以,如果你對於Event這個詞還是心存恐懼,那麼本文正是你需要的。讓我們從易到難,從具體到抽象,一步一步來解釋java事件處理機制。
一:觀察者模式
要了解事件和監聽,我們首先來必須要了解
觀察者模式
。
什麼是觀察者模式,我們先來看一個熟悉的場景:
1:教師佈置作業,
通知
學生;
2:學生觀察到老師佈置了作業,開始做作業;
首先我明確下,我歷來是十分反對以阿貓阿狗、老師學生這樣的場景來闡述程式碼問題的,所以該主題的最後肯定會回到實際程式碼中來。言歸正傳,在這個場景中,學生就是觀察者,教師就是被觀察者,但是大家一定要注意:
教師作為被觀察者,實際上是掌握著主動的,且看上文中我加粗的“通知”二字,因為這貌似看上去簡簡單單的通知,事實上卻要做很多事情(寫很多程式碼)。
好了,我們先來實現上面的場景:
程式碼:
觀察者,學生
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
public String getName() {
return this。name;
}
public Teacher(String name) {
this。name = name;
books = new ArrayList
}
public void setHomework(String homework) {
System。out。printf(“%s佈置了作業%s \n”, this。name, homework);
books。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);
看看結果吧:
注意,以上程式碼中,我直接使用了java。util包中的類Observable和介面Observer,我們當然也可以自己寫這兩個東東。
二:基礎版觀察者模式
初學者對於一上來就使用java。util中的api不習慣,可能覺得看不到摸不著,那我們就自己來寫一個基礎版的觀察者模式,大家感受下,在寫的過程中,一定要對照上節中的UML圖和程式碼,然後心中默唸:它們沒有區別,它們沒有區別!
上圖:
上程式碼:
觀察者,介面
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
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:觀察看上去是一個主動的行為,但是其實觀察者不是主動呼叫自己的
業務程式碼
的,相反,是被觀察者呼叫的。所以,觀察者模式還有另一個名字,叫釋出-訂閱模式,我認為,後者更貼切;
觀察者模式還有另外一種形態,就是
事件驅動模型
,這兩種方式在實現機制上是非常接近的,在理解了觀察者模式的基礎上,理解事件驅動,就非常簡單了。