在面向?qū)ο缶幊讨校^察者模式使用率非常高,最近流行的響應(yīng)式編程框架RxJava就對(duì)觀察者模式做出了擴(kuò)展。了解了觀察者模式也有助于對(duì)Rxjava的學(xué)習(xí)。
定義
觀察者模式中有兩個(gè)角色,被觀察者與觀察者,它們之間是一種一對(duì)多的依賴關(guān)系,當(dāng)被觀察者的狀態(tài)發(fā)生改變時(shí),觀察者也發(fā)生相應(yīng)的行為。在現(xiàn)實(shí)生活中很容易找出這樣的例子,比如教室里的教師和學(xué)生聽到下課鈴聲響了之后下課休息,教師和學(xué)生是觀察者,下課鈴是被觀察者,下課鈴響了也就是被觀察者的狀態(tài)發(fā)生改變,教師和學(xué)生這些觀察者做出相應(yīng)的行動(dòng)。另外,觀察者模式也可以稱為訂閱-發(fā)布模式,因?yàn)橛^察者與被觀察者的一系列活動(dòng)可以簡單概括為訂閱和發(fā)布。使用類圖表示觀察者模式,如下圖
subject可以是一個(gè)抽象類或普通類,concreteSubject是其子類,是具體的被觀察者,observer是一個(gè)只包含update(Object)方法的接口,實(shí)現(xiàn)該接口的ConcreteObserver為具體的觀察者。在jdk中就有內(nèi)置的類型Observable,Observer方便開發(fā)者使用觀察者模式。被觀察者需要繼承Observable類,而被觀察者則需要實(shí)現(xiàn)Observer接口。
再舉個(gè)例子,學(xué)生聽到廣播后做出行為。代碼如下:
觀察者Student
//Student.java
public class Student implements Observer {
private String name;
public Student(String name){
this.name = name;
}
@Override
public void update(Observable o, Object arg) {
System.out.println(name +","+ arg);
}
}
被觀察者Broadcast
// Broadcast.java
public class Broadcast extends Observable {
public void sendBroadcast(String content){
// 標(biāo)識(shí)狀態(tài)或內(nèi)容發(fā)生改變
setChanged();
// 通知所有觀察者
notifyObservers(content);
}
}
測試類
// Test.java
public class Test {
public static void main(String[] args) {
Broadcast broadcast = new Broadcast();
Student student1 = new Student("黃同學(xué)");
Student student2 = new Student("王同學(xué)");
Student student3 = new Student("楊同學(xué)");
brocast.addObserver(student1);
brocast.addObserver(student2);
brocast.addObserver(student3);
brocast.sendBroadcast("聽到廣播后請(qǐng)到辦公室一趟!");
}
}
結(jié)果輸出
楊同學(xué),聽到廣播后請(qǐng)到辦公室一趟!
王同學(xué),聽到廣播后請(qǐng)到辦公室一趟!
黃同學(xué),聽到廣播后請(qǐng)到辦公室一趟!
觀察者無需時(shí)刻觀察著被觀察者,就如以上兩個(gè)例子,學(xué)生無需時(shí)刻關(guān)注下課鈴聲以及廣播,只有當(dāng)被觀察者狀態(tài)改變,觀察者才能有下一步的行動(dòng)。
Observer和Observable的實(shí)現(xiàn)也很簡單,以下就是源碼:
Observer只是一個(gè)接口
// Observer.java
public interface Observer {
void update(Observable o, Object arg);
}
Observable需要確保相關(guān)操作是線程安全的,同時(shí)使用一個(gè)Vector來存放觀察者對(duì)象。
// Observable.java
public class Observable {
private boolean changed = false;
private Vector obs;
public Observable() {
obs = new Vector();
}
public synchronized void addObserver(Observer o) {
if (o == null)
throw new NullPointerException();
if (!obs.contains(o)) {
obs.addElement(o);
}
}
public synchronized void deleteObserver(Observer o) {
obs.removeElement(o);
}
public void notifyObservers() {
notifyObservers(null);
}
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);
}
public synchronized void deleteObservers() {
obs.removeAllElements();
}
protected synchronized void setChanged() {
changed = true;
}
protected synchronized void clearChanged() {
changed = false;
}
public synchronized boolean hasChanged() {
return changed;
}
public synchronized int countObservers() {
return obs.size();
}
}
可以看到,Observable內(nèi)部使用一個(gè)Vector來存儲(chǔ)觀察者對(duì)象,并且提供增加,刪除以及通知觀察者角色的方法,還有一點(diǎn)需要注意的就是,notifyObservers(Object arg)
方法內(nèi)部有一個(gè)判斷標(biāo)識(shí),因此具體的被觀察者角色需要先調(diào)用setChanged()
以標(biāo)識(shí)狀態(tài)發(fā)生改變,notifyObservers(Object arg)
方法才能有效。
當(dāng)然,我們完全可以自己實(shí)現(xiàn)抽象的主題角色和抽象的觀察者角色,只是jdk已經(jīng)幫我們實(shí)現(xiàn)了,無需我們重復(fù)造輪子而已。