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