觀察者模式


一、觀察者模式概述

定義:

觀察者模式(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個對象,使它們各自獨立的改變和復用。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM