觀察者模式定義了一種一對多的依賴關系,讓多個觀察者對象同時監聽某一個主題對象。這個主題對象在狀態上發生變化時,會通知所有觀察者對象,讓他們能夠自動更新自己。今天我們通過模擬按鈕的處理事件來深入Java觀察者模式的學習。
目錄導航
觀察者模式所涉及的角色有:
● 抽象主題(Subject)角色:抽象主題角色把所有對觀察者對象的引用保存在一個聚集(比如ArrayList對象)裡,每個主題都可以有任何數量的觀察者。抽象主題提供一個接口,可以增加和刪除觀察者對象,抽象主題角色又叫做抽象被觀察者(Observable)角色。
● 具體主題(ConcreteSubject)角色:將有關狀態存入具體觀察者對象;在具體主題的內部狀態改變時,給所有登記過的觀察者發出通知。具體主題角色又叫做具體被觀察者(Concrete Observable)角色。
● 抽象觀察者(Observer)角色:為所有的具體觀察者定義一個接口,在得到主題的通知時更新自己,這個接口叫做更新接口。
● 具體觀察者(ConcreteObserver)角色:存儲與主題的狀態自恰的狀態。具體觀察者角色實現抽象觀察者角色所要求的更新接口,以便使本身的狀態與主題的狀態 像協調。如果需要,具體觀察者角色可以保持一個指向具體主題對象的引用。
我們建立一個項目去開始今天的測試。項目結構如下:
demo很簡單,點擊button按鈕,修改button上顯示的文字
package com.huhx.frame;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
/**
* writer: huhx
*/
public class Jframe extends JFrame {
private JButton button;
public Jframe() {
button = new JButton("text");
add(button);
setVisible(true);
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
button.setText("world");
}
});
}
public static void main(String[] args) {
new Jframe();
}
}
java中的內置觀察者模式有兩個類:一個是observable被觀察者類,一個是observer觀察者接口。大體上與自定義模式相同,我們編寫程序時只需繼承obervable類,具體實現observer接口就可以了。值得注意的是:調用notifyObservers()方法之前,必須先調用setChanged()方法。這是因為observable類中把 changed變量初始化為false,notifyObservers()方法會先檢查該變量是否為true,如果不為ture,就不會調用 update()方法,這就要我們調用notifyObservers()之前,先用setChanged()方法將changed變量設置為true。
現在我們以上述例子為引子,使用Java中內置的Observer自己來模擬一個按鈕監聽事件的案例:
一、 創建一個繼承obervable類的被觀察者,在案例中也就是Button,類記為:MyButton
package com.huhx.observer;
import java.util.Observable;
import java.util.Observer;
/**
* @author huhx
*/
public class MyButton extends Observable {
public void addListeners(Observer observer) {
addObserver(observer);
}
public void process(Object args) {
setChanged();
notifyObservers(args);
}
public void process() {
process(null);
}
}
二、 創建幾個觀察這個按鈕的觀察者,其實就是按鈕的監聽事件:
模擬點擊事件:
package com.huhx.observer;
import java.util.Observable;
import java.util.Observer;
public class ClickObserver implements Observer {
@Override
public void update(Observable o, Object arg) {
if (arg != null) {
System.out.println("click: " + arg.toString());
} else {
System.out.println("click no arg");
}
}
}
模擬按下事件:
package com.huhx.observer;
import java.util.Observable;
import java.util.Observer;
public class PressObserver implements Observer {
@Override
public void update(Observable o, Object arg) {
if (arg != null) {
System.out.println("press: " + arg.toString());
} else {
System.out.println("press no arg");
}
}
}
三、 然後我們寫一個測試的類,用於模擬按鈕的事件:這裡的process方法模擬真實按鈕的觸發事件,比如我們按下按鈕。
package com.huhx.observer;
import java.util.Observable;
import java.util.Observer;
public class PersonTest {
public static void main(String[] args) {
MyButton button = new MyButton();
button.addObserver(new ClickObserver());
button.addListeners(new Observer() {
@Override
public void update(Observable o, Object arg) {
System.out.println("observer");
}
});
button.process("huhx");
}
}
五、 它的輸出結果如下:
observer click: huhx
上述我們通過案例,來模擬了按鈕的監聽事件原理,現在我們通過源碼來詳細的分析它的運行過程:
一、 Observer是一個觀察者可以實現的接口,它的文檔說明如下:
A class can implement the Observer interface when it wants to be informed of changes in observable objects.
Observer中有一個update()方法:
void update(Observable o, Object arg); // Observable 是觀察者 arg 是notifyObservers方法傳入的參數
二、 Observable是一個被觀察者繼承的類,它的文檔說明大致如下:
This class represents an observable object, or "data" in the model-view paradigm. It can be subclassed to represent an object that the application wants to have observed.
Observable有幾個重要的方法,我們簡單的介紹一下:
void addObserver(Observer o) // 加入觀察者到Vetor中 protected void clearChanged() // 設置change為false int countObservers() // 返回Vetor中觀察者的數量 void deleteObserver(Observer o) // 從Vetor中刪除指定的觀察者 void deleteObservers() // 刪除所有的觀察者 void notifyObservers() // 通知Vetor中的觀察者 void notifyObservers(Object arg) // 通知Vetor中的觀察者,帶參數 boolean hasChanged() // 判斷是否發生改變 protected void setChanged() // 設置changed為true
三、 接下來,我們結合上述的例子來分析Observer的原理:
當執行MyButton button = new MyButton(),由於MyButton是繼承Observable,所以下述代碼會執行。
private boolean changed = false;
private Vector obs;
public Observable() {
obs = new Vector();
}
當執行button.addObserver(new ClickObserver()),把參數中的Observer存放在上述初始化的Vetor中(為了適應多線程,用的是Vetor)
public synchronized void addObserver(Observer o) {
if (o == null)
throw new NullPointerException();
if (!obs.contains(o)) {
obs.addElement(o);
}
}
當執行button.process("huhx"),如果被觀察者發生了改變,就通知Vetor中的觀察者去執行update方法。
public void notifyObservers(Object arg) {
Object[] arrLocal;
synchronized (this) {
if (!changed)
return;
arrLocal = obs.toArray();
clearChanged();
}
for (int i = arrLocal.length-1; i>=0; i--)
((Observer)arrLocal[i]).update(this, arg);
}
上述案例中,我們還無法做到對觀察者的過濾通知,現在我們自己實現一個Observer,來對事件監聽的事件過濾,更好的模擬按鈕的事件機制。
一、 定義一個常量類,用於各種事件的標記:
package com.huhx.myobserver;
/**
* huhx 陳慧
*/
public class Event {
// 觸發所有的Listener
public static final int ALL = -1;
// 觸發內部定義的Listener
public static final int DEFAULT = 0;
public static final int CLICK = 1;
public static final int PRESS = 2;
public static final int DOWN = 3;
public static final int FOCUE = 4;
}
二、 定義一個Listener接口,和Java內置的Observer作用類似,注意方法中的參數是MyObservable
package com.huhx.listener;
import com.huhx.myobserver.MyObservable;
public interface Listener {
void actionPerformed(MyObservable o, Object arg);
}
三、 定義幾個實現Listener的觀察者,也就是事件:
模擬點擊事件:
package com.huhx.listener;
import com.huhx.myobserver.MyObservable;
/**
* 陳慧
* @author Linux
*
*/
public class ClickListener implements Listener {
@Override
public void actionPerformed(MyObservable o, Object arg) {
if (arg != null) {
System.out.println("click: " + arg);
} else {
System.out.println("click");
}
}
}
模擬按下事件:
package com.huhx.listener;
import com.huhx.myobserver.MyObservable;
public class PressListener implements Listener {
@Override
public void actionPerformed(MyObservable o, Object arg) {
System.out.println("press");
}
}
模擬定義在內部類中的事件:
package com.huhx.listener;
import com.huhx.myobserver.MyObservable;
public class DefaultListener implements Listener {
@Override
public void actionPerformed(MyObservable o, Object arg) {
}
}
四、 定義一個類似於Observable類的自定義被觀察者:
package com.huhx.myobserver;
import java.util.Vector;
import com.huhx.listener.ClickListener;
import com.huhx.listener.Listener;
import com.huhx.listener.PressListener;
/**
*
* @author huhx
*
*/
public class MyObservable {
private boolean changed = false;
private Vector<Listener> obs;
Object[] arrLocal;
public MyObservable() {
obs = new Vector<>();
}
protected synchronized void clearChanged() {
changed = false;
}
public void notifyObserverByEvent(int event, Object args) {
synchronized (this) {
if (!changed)
return;
arrLocal = obs.toArray();
clearChanged();
}
notifyFixObserver(event, args);
}
private void notifyFixObserver(int event, Object args) {
Listener listener = null;
for (int i = arrLocal.length - 1; i >= 0; i--) {
listener = (Listener) arrLocal[i];
switch (event) {
case Event.PRESS:
if (listener instanceof PressListener)
((PressListener) listener).actionPerformed(this, args);
break;
case Event.CLICK:
if (listener instanceof ClickListener)
((ClickListener) listener).actionPerformed(this, args);
break;
case Event.DEFAULT:
if (listener instanceof Listener)
listener.actionPerformed(this, args);
break;
case Event.ALL:
listener.actionPerformed(this, args);
break;
default:
break;
}
}
}
public void notifyObserverByEvent(int event) {
notifyObserverByEvent(event, null);
}
public void notifyObserverByEvent(Object args) {
notifyObserverByEvent(Event.ALL, args);
}
public synchronized void addListener(Listener listener) {
if (listener == null)
throw new NullPointerException();
if (!obs.contains(listener)) {
obs.addElement(listener);
}
}
protected synchronized void setChanged() {
changed = true;
}
}
五、 為了簡化Button的使用,我們仿造Java中的做法,先創建一個AbstractButton,用於處理共公的代碼:
package com.huhx.myobserver;
import com.huhx.listener.Listener;
public class AbstractButton extends MyObservable {
public void addListeners(Listener listener) {
addListener(listener);
}
public void process(int source, Object args) {
setChanged();
switch (source) {
case Event.CLICK:
notifyObserverByEvent(Event.CLICK, args);
break;
case Event.PRESS:
notifyObserverByEvent(Event.PRESS, args);
break;
case Event.DEFAULT:
notifyObserverByEvent(Event.DEFAULT, args);
break;
case Event.ALL:
notifyObserverByEvent(Event.ALL, args);
default:
break;
}
}
public void process(int source) {
process(source, null);
}
}
六、 創建一個被觀察者,也就是我們的按鈕Button
package com.huhx.myobserver;
/**
*
* @author Linux
*
*/
public class MyButtton extends AbstractButton {
}
七、 在測試中使用:
package com.huhx.myobserver;
import com.huhx.listener.ClickListener;
import com.huhx.listener.Listener;
public class PersonTest {
public static void main(String[] args) {
MyButtton buttton = new MyButtton();
buttton.addListener(new Listener() {
@Override
public void actionPerformed(MyObservable observable, Object object) {
System.out.println("listener");
System.out.println(observable + ", object: " + object.toString());
}
});
buttton.addListener(new ClickListener());
buttton.process(Event.CLICK);
// buttton.process(Event.DEFAULT, "world");
}
}
它的輸出結果如下:
click: 陳慧