摘要:觀察者模式是一種使用頻率非常高的設計模式,無論是移動應用應用或者桌面應用,觀察者模式幾乎無處不在,它為實現對象之間的聯動提供了一套完整的解決方案,凡是涉及到一對一或者一對多的對象交互場景都可以使用觀察者模式。
概述觀察者模式(Observer Pattern)屬于對象行為型模式的一種,定義對象之間的一種一對多依賴關系,使得每當一個對象狀態發生改變時,其相關依賴對象皆得到通知并被自動更新。
觀察者模式是一種使用率極高的模式,用于建立一種對象與對象之間的依賴關系,一個對象發生改變時將自動通知其他對象,其他對象將相應作出反應。在觀察者模式中,發生改變的對象稱為觀察目標,而被通知的對象稱為觀察者,一個觀察目標可以對應多個觀察者,而且這些觀察者之間可以沒有任何相互聯系,可以根據需要增加和刪除觀察者,使得系統更易于擴展。
觀察者模式的別名包括發布-訂閱(Publish/Subscribe)模式、模型-視圖(Model/View)模式、源-監聽器(Source/Listener)模式或從屬者(Dependents)模式。
案例前言:觀察者模式有兩種方模型,分別是推模型和拉模型
推模型: 主題對象向觀察者推送主題的詳細信息,不管觀察者是否需要,推送的信息通常是主題對象的全部或部分數據。該模式下如果推送數據變了觀察者都得改
拉模型: 主題對象在通知觀察者的時候,只傳遞少量信息。如果觀察者需要更具體的信息,由觀察者主動到主題對象中獲取,相當于是觀察者從主題對象中拉數據。一般這種 模型的實現中,會把主題對象自身通過update()方法傳遞給觀察者,這樣在觀察者需要獲取數據的時候,就可以通過這個引用來獲取了。
UML結構圖
抽象主題(Subject)角色: 將觀察者對象的引用保存在一個聚集(比如ArrayList對象)里,每個主題都可以有任何數量的觀察者。抽象主題提供接口,可以增加和刪除觀察者對象。抽象主題角色又叫做抽象被觀察者(Observable)角色。
具體主題(ConcreteSubject)角色: 將有關狀態存入具體觀察者對象;在具體主題的內部狀態改變時,給所有登記過的觀察者發出通知。具體主題角色又叫做具體被觀察者(Concrete Observable)角色。
抽象觀察者(Observer)角色: 為所有的具體觀察者定義一個更新接口,在得到主題的通知時更新自己。
具體觀察者(ConcreteObserver)角色: 觀察者的具體實現對象,實現抽象觀察者角色所要求的更新接口,以便使本身的狀態與主題的狀態相協調。如果需要,具體觀察者角色可以保持一個指向具體主題對象的引用。
推模式1.定義目標對象,它知道觀察它的觀察者,并提供注冊和刪除觀察者的接口
class Subject { /** * 用來保存注冊的觀察者對象 */ private Listobservers = new ArrayList<>(); /** * 注冊觀察者對象 * * @param observer 觀察者對象 */ void attach(Observer observer) { observers.add(observer); } /** * 通知所有注冊的觀察者對象 */ void notifyObservers(String newState) { for (Observer observer : observers) { observer.update(newState); } } }
2.具體的目標對象,負責把有關狀態存入到相應的觀察者對象,并在自己狀態發生改變時,通知各個觀察者
class ConcreteSubject extends Subject { private String subjectState; public String getSubjectState() { return subjectState; } public void change(String subjectState) { this.subjectState = subjectState; //狀態發生改變,通知各個觀察者 this.notifyObservers(subjectState); } }
3.創建觀察者接口,定義一個更新的接口給那些在目標發生改變的時候被通知的對象
interface Observer { /** * 更新的接口 * * @param subject 傳入目標對象,好獲取相應的目標對象的狀態 */ void update(String subject); }
4.具體觀察者對象,實現更新的方法,使自身的狀態和目標的狀態保持一致
class ConcreteObserver implements Observer { @Override public void update(String newState) { //具體的更新實現 //這里可能需要更新觀察者的狀態,使其與目標的狀態保持一致 System.out.println("接收到:" + newState); } }
5.創建推模型客戶端,用于測試
public class PushClient { public static void main(String[] args) { //創建主題對象 ConcreteSubject subject = new ConcreteSubject(); //創建觀察者對象 Observer observer = new ConcreteObserver(); //將觀察者對象登記到主題對象上 subject.attach(observer); //改變主題對象的狀態 subject.change("push state"); } }
6.運行結果
接收到:push state拉模式
1.定義目標對象,它知道觀察它的觀察者,并提供注冊和刪除觀察者的接口
class Subject { /** * 用來保存注冊的觀察者對象 */ private Listobservers = new ArrayList<>(); /** * 注冊觀察者對象 * * @param observer 觀察者對象 */ public void attach(Observer observer) { observers.add(observer); } /** * 通知所有注冊的觀察者對象 */ public void notifyObservers() { for (Observer observer : observers) { // 注意這句代碼" observer.update(this); } } }
2.具體的目標對象,負責把有關狀態存入到相應的觀察者對象,并在自己狀態發生改變時,通知各個觀察者
class ConcreteSubject extends Subject { /** * 示意,目標對象的狀態 */ private String subjectState; public String getSubjectState() { return subjectState; } public void change(String subjectState) { this.subjectState = subjectState; //狀態發生改變,通知各個觀察者 this.notifyObservers(); } }
3.創建觀察者接口,定義一個更新的接口給那些在目標發生改變的時候被通知的對象
interface Observer { /** * 更新的接口 * * @param subject 傳入目標對象,好獲取相應的目標對象的狀態 */ void update(Subject subject); }
4.具體觀察者對象,實現更新的方法,使自身的狀態和目標的狀態保持一致
class ConcreteObserver implements Observer { /** * 示意,觀者者的狀態 */ private String observerState; @Override public void update(Subject subject) { //具體的更新實現 //這里可能需要更新觀察者的狀態,使其與目標的狀態保持一致 observerState = ((ConcreteSubject) subject).getSubjectState(); System.out.println("接收到:" + observerState); } }
5.創建拉模型客戶端,用于測試
public class PullClient { public static void main(String[] args) { //創建主題對象 ConcreteSubject subject = new ConcreteSubject(); //創建觀察者對象 Observer observer = new ConcreteObserver(); //將觀察者對象登記到主題對象上 subject.attach(observer); //改變主題對象的狀態 subject.change("pull state"); } }
6.運行結果
接收到:pull state
上文說過推模型是假定主題對象知道觀察者需要的數據,這種模型下如果數據發生變更會造成極大的影響;而拉模型是主題對象不知道觀察者具體需要什么數據,沒有辦法的情況下,干脆把自身傳遞給觀察者,讓觀察者自己去按需要取值。由此可見:拉模式的適用范圍更廣;
JDK中應用對于觀察者模式,其實Java已經為我們提供了已有的接口和類。對于訂閱者(Subscribe,觀察者)Java為我們提供了一個接口。
UML圖
在JAVA語言的 java.util 庫里面,提供了一個Observable類以及一個Observer接口,構成JAVA語言對觀察者模式的支持。
Observer: 只定義了一個 update() 方法,當被觀察者對象的狀態發生變化時,被觀察者對象的 notifyObservers() 方法就會調用這一方法。
public interface Observer { void update(Observable o, Object arg); }
Observable: 充當觀察目標類,在Observable中定義了一個向量Vector來存儲觀察者對象。一個觀察目標類可以有多個觀察者對象,每個觀察者對象都是實現Observer接口的對象。在被觀察者發生變化時,會調用Observable的notifyObservers()方法,此方法調用所有的具體觀察者的update()方法, 從而使所有的觀察者都被通知更新自己。
setChanged() 設置一個內部標記變量,代表被觀察者對象的狀態發生了變化。
notifyObservers()調用所有登記過的觀察者對象的update()方法,使這些觀察者對象可以更新自己。
public class Observable { private boolean changed = false; //是否改變狀態,每次都需要設置,表示內容發生變化 private Vector小案例obs; //Vector利用同步方法來線程安全,線程安全在多線程情況下不會造成數據混亂 /** Construct an Observable with zero Observers. */ 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); } //通知方法,用于在方法內部循環調用向量中每一個觀察者的update()方法。 public void notifyObservers() { notifyObservers(null); } public void notifyObservers(Object arg) { Object[] arrLocal; synchronized (this) { if (!changed) //狀態值未改變時返回,不通知 return; arrLocal = obs.toArray(); //將Vector轉換成數組 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(); } }
1.定義兩個實現了實現java.util.Observer接口的觀察者
class SubscribeReader implements Observer { @Override public void update(Observable o, Object arg) { System.out.println("開始讀取:" + ((Publish) o).getMessage()); } } class SubscribeWrite implements Observer { @Override public void update(Observable o, Object arg) { System.out.println("開始寫入:" + ((Publish) o).getMessage()); } }
2.創建繼承java.util.Observable的通知者
class Publish extends Observable { private String message; public String getMessage() { return message; } public void setMessage(String message) { this.message = message; //改變通知者的狀態 super.setChanged(); //調用父類Observable方法,通知所有觀察者 super.notifyObservers(); } }
3.創建測試客戶端
public class Client { public static void main(String[] args) { Publish publish = new Publish(); // 遵循FIFO 模型 先進后出 SubscribeWrite write = new SubscribeWrite(); SubscribeReader reader = new SubscribeReader(); publish.addObserver(reader); publish.addObserver(write); publish.setMessage("Hello Battcn"); publish.setMessage("QQ:1837307557"); publish.setMessage("Email:1837307557@qq.com"); } }
4.運行結果
開始寫入:Hello Battcn 開始讀取:Hello Battcn 開始寫入:QQ:1837307557 開始讀取:QQ:1837307557 開始寫入:Email:1837307557@qq.com 開始讀取:Email:1837307557@qq.com觀察者模式與MVC
在當前流行的MVC(Model-View-Controller)架構中也應用了觀察者模式,MVC是一種架構模式,它包含三個角色:模型(Model),視圖(View)和控制器(Controller)。其中模型可對應于觀察者模式中的觀察目標,而視圖對應于觀察者,控制器可充當兩者之間的中介者。當模型層的數據發生改變時,視圖層將自動改變其顯示內容。
總結實現的關鍵是要建立觀察者和被觀察者之間的聯系、比如在被觀察者類中有個集合是用于存放觀察者的、當被檢測的東西發生改變的時候就要通知所有觀察者。在被觀察者中要提供一些對所有觀察者管理的一些方法.目的是添加或者刪除一些觀察者.這樣才能讓被觀察者及時的通知觀察者關系的狀態已經改變、并且調用觀察者通用的方法將變化傳遞過去。
在實現觀察者模式,如果JDK的Observable類和一個Observer接口能滿足需求,直接復用即可,無需自己編寫抽象觀察者、抽象主題類;
但是,java.util.Observable是一個類而不是接口,你必須設計一個類繼承它。如果某個類想同時具有Observable類和另一個超類的行為,由于java不支持多重繼承。所以這個時候就需要自己實現一整套觀察者模式。
優點
可實現表示層和數據邏輯層的分離,定義了穩定的消息更新傳遞機制,并抽象了更新接口,使得可以有各種各樣不同的表示層充當具體觀察者角色(Model/View)。
支持廣播通信,觀察目標會向所有已注冊的觀察者對象發送通知,簡化了一對多系統設計的難度(Publish/Subscribe)。
實現動態聯動。由于觀察者模式對觀察者注冊實行管理,那就可以在運行期間,通過動態的控制注冊的觀察者,來控制某個動作的聯動范圍,從而實現動態聯動。
缺點
如果一個被觀察者對象有很多直接和間接的觀察者,那么將所有的觀察者都通知到會花費很多時間。
如果在觀察者和被觀察者之間有循環依賴的話,被觀察者會觸發它們形成循環調用,可能導致系統崩潰。
觀察者模式沒有相應的機制讓觀察者知道被觀察者對象是怎么發生變化的,而僅僅只是知道被觀察者發生了變化。
觀察者模式是一種使用頻率非常高的設計模式,無論是移動應用、Web應用或者桌面應用,觀察者模式幾乎無處不在,它為實現對象之間的聯動提供了一套完整的解決方案,凡是涉及到一對一或者一對多的對象交互場景都可以使用觀察者模式。
說點什么參考文獻:http://www.cnblogs.com/JsonShare/p/7270546.html
全文代碼:https://gitee.com/battcn/design-pattern/tree/master/Chapter17/battcn-observer
個人QQ:1837307557
battcn開源群(適合新手):391619659
微信公眾號:battcn(歡迎調戲)
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/70769.html
摘要:在前面的文章中介紹過觀察者模式及并發編程的基礎知識,為了讓大家更好的了解觀察者模式故而特意寫了這篇番外概述在多線程下我們需要知道當前執行線程的狀態是什么比如運行,關閉,異常等狀態的通知,而且不僅僅是更新當前頁面。 在前面的文章中介紹過 觀察者模式 及 并發編程的基礎知識,為了讓大家更好的了解觀察者模式故而特意寫了這篇番外.. 概述 在Java多線程下,我們需要知道當前執行線程的狀態是...
摘要:懶漢非線程安全,需要用一定的風騷操作控制,裝逼失敗有可能導致看一周的海綿寶寶餓漢天生線程安全,的時候就已經實例化好,該操作過于風騷會造成資源浪費單例注冊表初始化的時候,默認單例用的就是該方式特點私有構造方法,只能有一個實例。 單例設計模式(Singleton Pattern)是最簡單且常見的設計模式之一,主要作用是提供一個全局訪問且只實例化一次的對象,避免多實例對象的情況下引起邏輯性錯...
摘要:設計模式的分類經典應用框架中常見的設計模式分為三類創建型模式對類的實例化過程的抽象。對象的結構模式是動態的。對象的行為模式則使用對象的聚合來分配行為。設計模式是個好東西,以后肯定還要進一步的學習,并且在項目中多實踐,提升自己的設計能力。 什么是設計模式? Christopher Alexander?說過:每一個模式描述了一個在我們周圍不斷重復發生的問題,以及該問題的解決方案的核心。這樣...
摘要:一個對象維持一系列依賴于它觀察者的對象,將有關狀態的任何變更自動通知給它們。觀察者模式的實現模擬擁有的一系列依賴使用擴展對象模擬目標和在觀察者列表上添加刪除或通知觀察者 一個對象(subject)維持一系列依賴于它(觀察者)的對象,將有關狀態的任何變更自動通知給它們。 當一個目標需要告訴觀察者發生了什么有趣的事情,它會向觀察者廣播一個通知 當我們不再希望某個特定的觀察者獲取其注冊目...
摘要:扎實基礎幸好自己之前花了大力氣去給自己打基礎,讓自己現在的基礎還算不錯。 寫文章不容易,點個贊唄兄弟專注 Vue 源碼分享,文章分為白話版和 源碼版,白話版助于理解工作原理,源碼版助于了解內部詳情,讓我們一起學習吧研究基于 Vue版本 【2.5.17】 如果你覺得排版難看,請點擊 下面鏈接 或者 拉到 下面關注公眾號也可以吧 【Vue原理】Vue源碼閱讀總結大會 - 序 閱讀源碼是需...
閱讀 2731·2021-11-24 09:39
閱讀 1647·2021-09-28 09:35
閱讀 1119·2021-09-06 15:02
閱讀 1306·2021-07-25 21:37
閱讀 2726·2019-08-30 15:53
閱讀 3643·2019-08-30 14:07
閱讀 714·2019-08-30 11:07
閱讀 3512·2019-08-29 18:36