上篇我們講解了觀察者模式的一些知識,而且自定義觀察者模式的經典程式碼,(傳送們:設計模式走一遍—-觀察者模式(上))
這篇簡單講一下JDK自帶的觀察者模式實現程式碼。
對於觀察者模式,JDK中提供了一個Observer介面(觀察者),一個Observable類(主題物件)。
注:被觀察者又被稱為主題物件,目標物件。
具體我們來看下原始碼。
1.觀察者介面
public interface Observer {
/**
* This method is called whenever the observed *object is changed.
*當被觀察者發生變化時,該方法將會被呼叫
* @param o the observable object.
* @param arg an argument passed to the <code>notifyObservers</code>
* method.
*/
void update(Observable o, Object arg);
}
該介面相當於觀察者,裡面有一個update(Observable o, Object arg)方法,Observable引數是指主題物件,該引數指明該觀察者是屬於哪一個主題物件的。
arg引數可以是任意物件,假如主題物件在傳送通知時,想要傳遞什麼資料給觀察者,那麼就可以把資料物件傳遞給arg引數。
2.主題物件類(方法有點多,我就不放英文解釋了)
//主題物件可以是介面、抽象類、具體類,我們上節說
//一般採用抽象類,不過JDK這裡使用的是具體類
public class Observable {
//標記主題物件的狀態是否改變
private boolean changed = false;
//存放觀察者集合,之所以用Vector而不用ArrayList
//主要是Vector是執行緒安全的
private Vector<Observer> 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);
}
//標記該物件的狀態是否傳送了改變
protected synchronized void setChanged() {
changed = true;
}
//指示該物件不會再發生改變,或者它已經通知了
//所有觀察者
protected synchronized void clearChanged() {
changed = false;
}
//測試物件是否發生了改變。當且僅當在此物件最近
//呼叫了setChange()方法
public synchronized boolean hasChanged() {
return changed;
}
//如果hasChanged()方法指示此物件傳送了改變,
//則通知所有觀察者,並且呼叫clearChanged()方法
//指示此物件不再改變
public void notifyObservers() {
notifyObservers(null);
}
//與上面沒有引數的同名方法相同,只是如果這個方
//法的arg引數可以接受主題物件想要傳遞觀察者的資料物件
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();
}
//返回觀察者的數量
public synchronized int countObservers() {
return obs.size();
}
}
該具體類Observable相當於主題物件,實現的主要功能就是當自己的狀態傳送改變時,通知觀察者,觀察者再根據通知,在update方法做出相應的反應。
簡單寫個Demo測試下。
public class Test {
public static void main(String[] args){
//建立一個主題物件
AnimalSubject animalSubject = new AnimalSubject();
animalSubject.addObserver(new DogObsever());
animalSubject.addObserver(new LionObsever());
//狀態發生改變
animalSubject.setChanged();
//通知觀察者
animalSubject.notifyObservers();
}
}
//動物主題,弄子類方便擴充主題物件功能
class AnimalSubject extends Observable{
//不過我就不新增程式碼、方法了
//不覆蓋下的話,上面的測試呼叫不了setChange()方法
//為了方便測試,覆蓋重寫下
@Override
protected synchronized void setChanged() {
super.setChanged();
}
}
class DogObsever implements Observer{
@Override
public void update(Observable o, Object arg) {
System.out.println("收到通知,小狗觀察者正在做出相應處理");
}
}
class LionObsever implements Observer{
@Override
public void update(Observable o, Object arg) {
System.out.println("收到通知,獅子觀察者正在做出相應處理");
}
}
列印結果
收到通知,獅子觀察者正在做出相應處理
收到通知,小狗觀察者正在做出相應處理
從上面的程式碼中我們可以發現JDk內建的觀察者模式中的主題物件是一個具體類,而不是一個抽象類或介面,而且setChange()方法還被保護起來了(被定義為protected),這就意味著,要在別的類中呼叫該方法,那麼我們必須繼承在子類中重寫覆蓋該方法。顯然,我覺得這很不友好…..
可能這也是JDK內建的觀察者模式很少被拿來使用 的原因吧,一般都是自己來自定義觀察者模式。
希望大家能夠動手寫一下這些程式碼,可能會碰到一些你沒想到的問題。
完
關注公我的眾號:苦逼的碼農,獲取更多原創文章,後臺回覆禮包送你一份時下熱門的資源大禮包。同時也感謝把文章介紹給更多需要的人