一、觀察者模式概述
定義:
觀察者模式(Observer Pattern):定義對象之間的一種一對多的依賴關系,使得每當一個對象狀態發生改變時,其相關依賴對象皆得到通知並被自動更新。別名有:發布-訂閱(Publish/Subscribe)模式、模型-視圖(Model/View)模式、源-監聽器(Source/Listener)模式、從屬者(Dependents)模式。

4個角色:
Subject(目標):被觀察的對象。維護觀察者集合(增刪),定義通知觀察者方法notify()。
ConcreteSubject(具體目標):包含經常發生改變的數據,變化時,觸發notify所有觀察者。
Observer(觀察者):接口聲明update()更新方法
ConcreteObserver(具體觀察者):維護一個指向具體目標對象的引用,存儲關心的數據,當具體目標數據變化時觸發update(),更新數據以保持和目標數據一致。
二、JDK對觀察者模式的支持

如上圖所示,由於觀察者模式被多次使用, JDK源碼就封裝在rt.jar里面的java.util包下面,和頂級類Objects、各種java集合在一個級別,這種待遇驚呆了我。看來不好好剖析一下是不行了。
1.Observable類
充當觀察目標類,用Vector集合來存儲觀察者(預留一張飛機票:Vector集合詳解)。
封裝方法:增加、刪除觀察者、維護是否變化、通知觀察者。(值得一提的是這些方法全部用synchronized修飾解決了並發臟數據問題)
2.Observer接口
充當抽象觀察者,聲明了一個update接口,當觀察目標Observable的子類發生變化時,調用notifyObservers方法通知觀察者,執行具體觀察者實現類中的update方法。
3.測試一下:
1 package designPatterns.observerPattern; 2 3 import java.util.Observable; 4 import java.util.Observer; 5 6 /** 7 * 8 * @ClassName:MyObserver 9 * @Description:觀察者 10 * @author denny.zhang 11 * @date 2018年1月23日上午10:50:13 12 */ 13 public class MyObserver implements Observer { 14 15 private String name; 16 17 public MyObserver(Observable o, String name) { 18 o.addObserver(this); 19 this.name = name; 20 } 21 22 @Override 23 public void update(Observable o, Object arg) { 24 System.out.println("觀察者" + name + "觸發更新!arg=" + arg + ",目標的觀察者數量=" + o.countObservers()); 25 } 26 27 }
1 package designPatterns.observerPattern; 2 3 import java.util.Observable; 4 5 /** 6 * 7 * @ClassName:MyObserverable 8 * @Description:觀察目標 9 * @author denny.zhang 10 * @date 2018年1月23日上午10:45:26 11 */ 12 public class MyObserverable extends Observable { 13 //觀察目標初始數據 14 private String data = "data0"; 15 16 public String getData() { 17 return data; 18 } 19 20 public void setData(String data) { 21 //如果數據變化了 22 if (!this.data.equals(data)) { 23 this.data = data; 24 setChanged();//置變化狀態為true 25 } 26 notifyObservers(data);//通知所有觀察者 27 } 28 29 }
1 package designPatterns.observerPattern; 2 3 /** 4 * 5 * @ClassName:MyTest 6 * @Description:利用JDK自帶觀察者Observer接口和,目標Observable類,測試樣例。 7 * @author denny.zhang 8 * @date 2018年1月23日下午12:49:53 9 */ 10 public class MyTest { 11 12 public static void main(String[] args) { 13 //構造觀察目標 14 MyObserverable observerable = new MyObserverable(); 15 //構造2個觀察者實現類:添加觀察者進觀察目標 16 MyObserver observer1 = new MyObserver(observerable, "1"); 17 MyObserver observer2 = new MyObserver(observerable, "2"); 18 System.out.println("===1.目標初始值=" + observerable.getData()); 19 System.out.println("===2.給目標設置不同值[data1],看觀察者是否觸發更新!=========="); 20 //設置目標值 21 observerable.setData("data1"); 22 System.out.println("===3.給目標設置想同值[data1],看觀察者是否觸發更新!=========="); 23 //設置目標相同值 24 observerable.setData("data1"); 25 } 26 }
運行結果:
===1.目標初始值=data0 ===2.給目標設置不同值[data1],看觀察者是否觸發更新!========== 觀察者2觸發更新!arg=data1,目標的觀察者數量=2 觀察者1觸發更新!arg=data1,目標的觀察者數量=2 ===3.給目標設置想同值[data1],看觀察者是否觸發更新!==========
看上面運行結果,初始data=data0,當觀察者目標設置data1時,setChanged()觸發Observable類變量changed=true,然后觸發notifyObservers()通知所有的觀察者,執行update方法。如下圖:
1 public void notifyObservers(Object arg) { 6 Object[] arrLocal; 7 8 synchronized (this) { 21 if (!changed)//如果沒變化,直接返回 22 return; 23 arrLocal = obs.toArray();//如果變化了,重新獲取觀察者數組 24 clearChanged();//關閉變化 25 } 26 //遍歷所有觀察者,觸發更新方法 27 for (int i = arrLocal.length-1; i>=0; i--) 28 ((Observer)arrLocal[i]).update(this, arg); 29 }
三、應用實例
1.java事件驅動模型
JDK1.1后的版本中,事件驅動模型采用基於觀察者模式的委派事件模型(Delegation Event Model,DEM),即把事件委派給獨立的事件處理對象(Listener)。
分3個角色:
1)事件發布者(事件源):Event Source,充當觀察者模式的觀察目標
2)事件對象:Event Object
3)事件監聽器:Event Listener,充當觀察者模式的觀察者
如下圖所示,就是一個典型的DEM模型,Spring-context源碼包,具體應用是在Spring-boot容器啟動流程中觸發各種6種定義好的事件。用戶可以實現接口:ApplicationListener<ApplicationReadyEvent>(不一定是ApplicationReadyEvent,共定義了6種事件),然后復寫onApplicationEvent方法,實現自己的業務邏輯。飛機票:spring boot容器啟動詳解

2.MVC架構
MVC(Model-View-Controller)架構是當前流行架構之一,也應用了觀察者模式。角色如下:
1.模型(Model):充當觀察目標
2.視圖(View):充當觀察者
3.控制器(Controller)
這里更多的是一種思想,暫時還沒找到Spring MVC源碼來支撐。
四、總結
1.優點
1)把觀察目標和觀察者之間建立一個抽象的耦合, 觀察目標只需要維護觀察者集合即可,並不依賴於觀察者。
2)滿足開閉原則,新增觀察者無需修改觀察目標代碼。
3)支持廣播通信,觀察目標會通知所有觀察者。實現了1對多通信。
2.缺點
1)如果觀察者過多,會影響觀察目標的性能。因為需要通知所有觀察者。
2)觀察模式不可濫用,過多導致程序邏輯復雜,分析問題難度增加。
3.適用場景
適合某些"交互定義良好"的場合使用,例如:
1)一個對象的改變導致其它一個或者多個對象也發生改變,且並不知道有多少個對象需要發生改變。
2)一個抽象模型有2個方面,其中一個方面依賴另一個方面,獨立封裝為2個對象,使它們各自獨立的改變和復用。
