介紹(以下部分內容來自維基百科)
定義對象間的一種一對多的依賴關係,當一個對象的狀態發生改變時,所有依賴於它的對象都得到通知並被自動更新。
這話聽着很拗口,其實就是多個對象依賴於一個對象,並按照他的狀態變化改變自身.
結構
主要有四個類:
- 抽象目標類(接口).
- 具體的目標類.
- 抽象的觀察者類.
- 具體的觀察者類.
具體場景及代碼演示
你現在是一枚老韭菜!還是會寫代碼的韭菜!(簡單易割還長得快).
你每天都炒股,只盯着兩隻股票,一個是阿里巴巴
,一個是騰訊
.你想在任何你能接觸到的終端上面隨時的看到最新的股市消息.
因此你開發了下面的代碼(僞代碼):
if 阿里巴巴 有變化
send (newest info) to 手機
send (newest info) to 電腦
send (newest info) to 手錶
這樣子沒啥問題,但是以後想要修改的時候,比如你明天新買了個pad(韭菜哪有錢…),那你就需要修改上述的代碼.
這樣不是很符合開閉原則,因此使用觀察者模式
將他重構一下.
由於jdk在util包裏有對抽象目標
和抽象觀察者
的定義,這裏不做重複的定義,學習一下這兩個接口.
抽象目標(被觀察者)
package java.util;
public class Observable {
private boolean changed = false;
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);
}
public void notifyObservers() {
notifyObservers(null);
}
//通知觀察者
public void notifyObservers(Object arg) {
/*
* a temporary array buffer, used as a snapshot of the state of
* current Observers.
*/
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 int countObservers() {
return obs.size();
}
}
可以看出,他定義並實現了結構圖中的三個方法,添加觀察者
,刪除觀察者
,通知觀察者
.存儲觀察者使用的Vector.保證對觀察者的添加及刪除操作線程安全.對Vector想了解的朋友可以移步Vector源碼閱讀.
抽象觀察者
public interface Observer {
//更新自身
void update(Observable o, Object arg);
}
Observer
只定義了一個方法,就是更新自身.
好,那麼到了重頭戲了,我們通過自己的實現來重構上面的項目.
股票類(具體的某個被觀察者)
package com.huyan.demo.observer;
import java.util.Observable;
import java.util.Observer;
/**
* created by huyanshi on 2018/12/28
*/
public class Stock extends Observable {
private String name;
public Stock(String name) {
this.name = name;
}
@Override
public synchronized void addObserver(Observer o) {
super.addObserver(o);
}
@Override
public synchronized void deleteObserver(Observer o) {
super.deleteObserver(o);
}
@Override
public void notifyObservers(Object arg) {
super.notifyObservers(arg);
}
public void stockUp() {
System.out.println("漲了漲了!牛逼!");
super.setChanged();
}
@Override
public String toString() {
return "這裏是股票:" + name;
}
}
我們定義並實現了Stock類,擁有屬性:名字.
以及幾個重寫的方法,其實完全調用了父類的方法,這裏只是寫出來方便看.
此外定義了一個股票上漲
的方法,在這個方法裏面調用了父類的serChange
方法,代表着此時,被觀察者發生了變化.
具體觀察者(PC和手機)
public class PC implements Observer {
@Override
public void update(Observable o, Object arg) {
System.out.println("------------PC已經更新.");
System.out.println(arg.toString());
}
}
public class Phone implements Observer {
@Override
public void update(Observable o, Object arg) {
System.out.println("------------Phone已經更新.");
System.out.println(arg.toString());
}
}
這裏定義了PC和手機,實現了唯一的update
方法,在裏面更新自己信息並打印,標識自己被通知到了
.
測試
代碼
@Test
public void test(){
//定義一個股票
Stock stock = new Stock("阿里巴巴");
//給它添加觀察者
stock.addObserver(new PC());
stock.addObserver(new Phone());
//這裏阿里巴巴股票大漲!
stock.stockUp();
//通知所有觀察者
stock.notifyObservers("阿里巴巴大漲0.01%");
}
這裏我們定義了一個股票(被觀察者),然後給它添加了手機和PC(觀察者),之後股票上漲(被觀察者變化),看一下結果,觀察者是否收到消息並更新自身信息.
輸出結果
漲了漲了!牛逼!
------------Phone已經更新.
阿里巴巴大漲0.01%
------------PC已經更新.
阿里巴巴大漲0.01%
可以看到,這裏在被觀察者發生變化後,所有(兩個)的觀察者都受到了消息並且做出了反應.
如何擴展呢?
這樣重構完之後,擴展變得十分方便,比如我們新買了pad.只需要新建一個Pad
類,然後在添加觀察者步驟,將其添加即可.
想換一隻股票觀察或者添加一直股票,只需要new一個股票的對象重複上述操作即可.
總結
在對象觀察一對多
的情況下,觀察者模式成功的將我們的系統解耦,並提高了系統的可擴展性,但是有沒有問題呢?
有,如果這裏的終端不只是個人的,而是一羣人的,即:觀察者過多,一個個通知將會很慢.
因此,在發現場景適合後,仍需做一些調研,纔可以對代碼進行重寫,千萬不要得不償失.
完。
ChangeLog
2018-12-28 完成以上皆爲個人所思所得,如有錯誤歡迎評論區指正。
歡迎轉載,煩請署名並保留原文鏈接。
聯繫郵箱:[email protected]
更多學習筆記見個人博客------>呼延十