0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

觀察者模式,超詳細(xì)!

馬哥Linux運(yùn)維 ? 來源:馬哥Linux運(yùn)維 ? 2023-08-21 16:06 ? 次閱讀

設(shè)計(jì)模式提供了軟件開發(fā)過程中的一些最佳實(shí)踐,可以幫助我們解決常見的編程問題,提高軟件的可維護(hù)性和可復(fù)用性,并使我們的代碼更加健壯和靈活。設(shè)計(jì)模式可以帶來以下好處:提高代碼的可讀性和可維護(hù)性、提高軟件的可復(fù)用性、提高開發(fā)效率、提高系統(tǒng)的靈活性和可擴(kuò)展性。今天我們講一下觀察者模式的具體應(yīng)用。

觀察者模式是一種軟件設(shè)計(jì)模式,它允許一個(gè)對象(稱為“主題”)管理其依賴項(xiàng)(稱為“觀察者”),它定義了對象之間的一對多依賴關(guān)系,當(dāng)一個(gè)對象狀態(tài)發(fā)生改變時(shí),其相關(guān)依賴項(xiàng)將會自動收到通知。這種模式提供了一種靈活的方式,將一個(gè)對象的狀態(tài)與依賴它的多個(gè)對象聯(lián)系起來。

fb6d19f2-3f6f-11ee-ac96-dac502259ad0.png

在觀察者模式中,主題和觀察者之間建立了一種訂閱關(guān)系。主題負(fù)責(zé)維護(hù)其狀態(tài)并提供一個(gè)注冊表,用于存儲與其相關(guān)聯(lián)的觀察者對象。當(dāng)主題的狀態(tài)發(fā)生改變時(shí),它會自動通知所有與之相關(guān)聯(lián)的觀察者,并傳遞相應(yīng)的參數(shù)。觀察者接收到通知后,可以執(zhí)行相應(yīng)的操作來響應(yīng)主題狀態(tài)的改變。

意圖

觀察者模式是一種行為設(shè)計(jì)模式允許你定義一種訂閱機(jī)制,可在對象事件發(fā)生時(shí)通知多個(gè)觀察該對象的其他對象。

問題

假如你有兩種類型的對象顧客商店顧客對某個(gè)特定品牌產(chǎn)品非常感興趣例如最新型號的 iPhone 手機(jī),而該產(chǎn)品很快將會在商店里出售。

顧客可以每天來商店看看產(chǎn)品是否到貨。但如果商品尚未到貨時(shí)絕大多數(shù)來到商店的顧客都會空手而歸。

另一方面,每次新產(chǎn)品到貨時(shí),商店可以向所有顧客發(fā)送郵件可能會被視為垃圾郵件。這樣,部分顧客就無需反復(fù)前往商店了,但也可能會惹惱對新產(chǎn)品沒有興趣的其他顧客。

我們似乎遇到了一個(gè)矛盾要么讓顧客浪費(fèi)時(shí)間檢查產(chǎn)品是否到貨要么讓商店浪費(fèi)資源去通知沒有需求的顧客。

解決方案

擁有一些值得關(guān)注的狀態(tài)的對象通常被稱為標(biāo),由于它要將自身的狀態(tài)改變通知給其他對象我們也將其稱為發(fā)publisher。所有希望關(guān)注發(fā)布者狀態(tài)變化的其他對象被稱為subscribers

觀察者模式建議你為發(fā)布者類添加訂閱機(jī)制,讓每個(gè)對象都能訂閱或取消訂閱發(fā)布者事件流不要害怕這并不像聽上去那么復(fù)雜。實(shí)際上該機(jī)制包括 1一個(gè)用于存儲訂閱者對象引用的列表成員變量;2幾個(gè)用于添加或刪除該列表中訂閱者的公有方法

fbd70240-3f6f-11ee-ac96-dac502259ad0.png

訂閱機(jī)制允許對象訂閱事件通知。

現(xiàn)在無論何時(shí)發(fā)生了重要的發(fā)布者事件,它都要遍歷訂閱者并調(diào)用其對象的特定通知方法

實(shí)際應(yīng)用中可能會有十幾個(gè)不同的訂閱者類跟蹤著同一個(gè)發(fā)布者類的事件,你不會希望發(fā)布者與所有這些類相耦合此外如果他人會使用發(fā)布者類,那么你甚至可能會對其中的一些類一無所知。

因此所有訂閱者都必須實(shí)現(xiàn)同樣的接口,發(fā)布者僅通過該接口與訂閱者交互。接口中必須聲明通知方法及其參數(shù),這樣發(fā)布者在發(fā)出通知時(shí)還能傳遞一些上下文數(shù)據(jù)。

fbf1fa0a-3f6f-11ee-ac96-dac502259ad0.png

發(fā)布者調(diào)用訂閱者對象中的特定通知方法來通知訂閱者

如果你的應(yīng)用中有多個(gè)不同類型的發(fā)布者,且希望訂閱者可兼容所有發(fā)布者那么你甚至可以進(jìn)一步讓所有發(fā)布者遵循同樣的接口。該接口僅需描述幾個(gè)訂閱方法即可這樣訂閱者就能在不與具體發(fā)布者類耦合的情況下通過接口觀察發(fā)布者的狀態(tài)。

真實(shí)世界類比

如果你訂閱了一份雜志或報(bào)紙,那就不需要再去報(bào)攤查詢新出版的刊物了。出版社即應(yīng)用中的發(fā)布者會在刊物出版后甚至提前直接將最新一期寄送至你的郵箱中。

出版社負(fù)責(zé)維護(hù)訂閱者列表,了解訂閱者對哪些刊物感興趣。當(dāng)訂閱者希望出版社停止寄送新一期的雜志時(shí)他們可隨時(shí)從該列表中退出。

我們看一段代碼示例,然后再通過示例進(jìn)行分析。在JavaScript中,我們可以使用原型或類來實(shí)現(xiàn)觀察者模式。下面是一個(gè)使用原型的實(shí)現(xiàn)示例:

// 觀察者接口  
var Observer = function() {};  


Observer.prototype.update = function(data) {};  


// 具體觀察者  
var ConcreteObserver1 = function() {};  
ConcreteObserver1.prototype = Object.create(Observer.prototype);  
ConcreteObserver1.prototype.constructor = ConcreteObserver1;  
ConcreteObserver1.prototype.update = function(data) {  
  console.log('ConcreteObserver1 received data: ' + data);  
};  


// 具體觀察者  
var ConcreteObserver2 = function() {};  
ConcreteObserver2.prototype = Object.create(Observer.prototype);  
ConcreteObserver2.prototype.constructor = ConcreteObserver2;  
ConcreteObserver2.prototype.update = function(data) {  
  console.log('ConcreteObserver2 received data: ' + data);  
};  


// 主題  
var Subject = function() {  
  this.observers = [];  
};  


Subject.prototype.registerObserver = function(observer) {  
  this.observers.push(observer);  
};  


Subject.prototype.notifyObservers = function(data) {  
  for (var i = 0; i < this.observers.length; i++) {  
    this.observers[i].update(data);  
  }  
};  


// 具體主題  
var ConcreteSubject = function() {};  
ConcreteSubject.prototype = Object.create(Subject.prototype);  
ConcreteSubject.prototype.constructor = ConcreteSubject;  
ConcreteSubject.prototype.setState = function(data) {  
  this.notifyObservers(data);  
};

在上面的代碼中,我們首先定義了一個(gè)Observer接口,它包含一個(gè)update方法。然后我們創(chuàng)建了兩個(gè)具體的觀察者ConcreteObserver1和ConcreteObserver2,它們都實(shí)現(xiàn)了Observer接口的update方法。接著我們定義了一個(gè)主題Subject,它包含一個(gè)觀察者數(shù)組和一個(gè)注冊方法registerObserver,以及一個(gè)通知方法notifyObservers。最后我們創(chuàng)建了一個(gè)具體主題ConcreteSubject,它繼承了Subject的原型并實(shí)現(xiàn)了一個(gè)setState方法,該方法調(diào)用通知方法來通知所有觀察者狀態(tài)改變。

在上面的示例中,我們使用了原型繼承來實(shí)現(xiàn)Observer接口和具體的觀察者類。在實(shí)際應(yīng)用中,我們也可以使用類繼承或ES6的class語法來實(shí)現(xiàn)這些類。另外,在具體主題ConcreteSubject中,我們通過調(diào)用notifyObservers方法來通知所有觀察者狀態(tài)改變,這個(gè)方法可以傳遞一個(gè)參數(shù)作為通知的內(nèi)容。在具體觀察者的update方法中,我們可以根據(jù)傳遞的參數(shù)來執(zhí)行相應(yīng)的操作。

除了使用JavaScript實(shí)現(xiàn)觀察者模式外,我們還可以在其他編程語言和框架中找到這種模式的實(shí)現(xiàn)。例如,Redis的訂閱模型和WebSocket請求都使用了類似的方式來實(shí)現(xiàn)主題和觀察者之間的訂閱關(guān)系。這些實(shí)現(xiàn)方式都允許客戶端訂閱特定主題,并在主題狀態(tài)發(fā)生改變時(shí)自動接收通知。

觀察者模式適合應(yīng)用場景

當(dāng)一個(gè)對象狀態(tài)的改變需要改變其他對象或?qū)嶋H對象是事先未知的或動態(tài)變化的時(shí),可使用觀察者模式

當(dāng)你使用圖形用戶界面類時(shí)通常會遇到一個(gè)問題。比如,你創(chuàng)建了自定義按鈕類并允許客戶端在按鈕中注入自定義代碼這樣當(dāng)用戶按下按鈕時(shí)就會觸發(fā)這些代碼。

觀察者模式允許任何實(shí)現(xiàn)了訂閱者接口的對象訂閱發(fā)布者對象的事件通知。你可在按鈕中添加訂閱機(jī)制允許客戶端通過自定義訂閱類注入自定義代碼。

當(dāng)應(yīng)用中的一些對象必須觀察其他對象時(shí),可使用該模式。但僅能在有限時(shí)間內(nèi)或特定情況下使用。

訂閱列表是動態(tài)的因此訂閱者可隨時(shí)加入或離開該列表。

實(shí)現(xiàn)方式

  1. 仔細(xì)檢查你的業(yè)務(wù)邏輯,試著將其拆分為兩個(gè)部分獨(dú)立于其他代碼的核心功能將作為發(fā)布者;其他代碼則將轉(zhuǎn)化為一組訂閱類

  2. 聲明訂閱者接口。該接口至少應(yīng)聲明一個(gè)update方法。

  3. 聲明發(fā)布者接口并定義一些接口來在列表中添加和刪除訂閱對象。記住發(fā)布者必須僅通過訂閱者接口與它們進(jìn)行交互。

  4. 確定存放實(shí)際訂閱列表的位置并實(shí)現(xiàn)訂閱方法。通常所有類型的發(fā)布者代碼看上去都一樣,因此將列表放置在直接擴(kuò)展自發(fā)布者接口的抽象類中是顯而易見的。具體發(fā)布者會擴(kuò)展該類從而繼承所有的訂閱行為。

    但是,如果你需要在現(xiàn)有的類層次結(jié)構(gòu)中應(yīng)用該模式則可以考慮使用組合的方式將訂閱邏輯放入一個(gè)獨(dú)立的對象,然后讓所有實(shí)際訂閱者使用該對象。

  5. 創(chuàng)建具體發(fā)布者類。每次發(fā)布者發(fā)生了重要事件時(shí)都必須通知所有的訂閱者

  6. 在具體訂閱者類中實(shí)現(xiàn)通知更新的方法。絕大部分訂閱者需要一些與事件相關(guān)的上下文數(shù)據(jù)。這些數(shù)據(jù)可作為通知方法的參數(shù)來傳遞

    但還有另一種選擇。訂閱者接收到通知后直接從通知中獲取所有數(shù)據(jù)。在這種情況下,發(fā)布者必須通過更新方法將自身傳遞出去。另一種不太靈活的方式是通過構(gòu)造函數(shù)將發(fā)布者與訂閱者永久性地連接起來

  7. 客戶端必須生成所需的全部訂閱者,并在相應(yīng)的發(fā)布者處完成注冊工作。

觀察者模式優(yōu)缺點(diǎn)

  • 優(yōu)點(diǎn):

    • 降低目標(biāo)與觀察者之間的耦合關(guān)系

    • 支持“廣播通信

    • 符合開閉原則。

  • 確定:

    • 通知可能會花費(fèi)很長時(shí)間

    • 循環(huán)依賴的問題

    • 沒有相應(yīng)的機(jī)制讓觀察者知道所觀察的目標(biāo)對象是怎么發(fā)生變化的

總結(jié)一下,觀察者模式適用于任何需要實(shí)現(xiàn)一對多依賴關(guān)系的場景,使得主題的狀態(tài)改變可以自動通知給所有的觀察者。

  1. 訂閱/發(fā)布系統(tǒng):觀察者模式可以用于實(shí)現(xiàn)訂閱/發(fā)布系統(tǒng)。主題可以代表各種事件或消息,觀察者可以訂閱感興趣的主題并接收相關(guān)的通知。

  2. 實(shí)時(shí)通信:觀察者模式可以用于實(shí)現(xiàn)實(shí)時(shí)通信。當(dāng)某個(gè)事件發(fā)生時(shí),相關(guān)的觀察者可以立即得到通知并做出相應(yīng)的響應(yīng)。

  3. 數(shù)據(jù)綁定:在圖形用戶界面開發(fā)中,觀察者模式可以用于實(shí)現(xiàn)數(shù)據(jù)綁定。當(dāng)某個(gè)數(shù)據(jù)源發(fā)生改變時(shí),相關(guān)的視圖可以自動更新。

  4. 事件驅(qū)動系統(tǒng):觀察者模式可以用于實(shí)現(xiàn)事件驅(qū)動系統(tǒng)。當(dāng)某個(gè)事件觸發(fā)時(shí),相關(guān)的觀察者可以收到通知并執(zhí)行相應(yīng)的操作。

  5. 異步消息處理:在分布式系統(tǒng)中,觀察者模式可以用于實(shí)現(xiàn)異步消息處理。當(dāng)某個(gè)消息到達(dá)時(shí),相關(guān)的觀察者可以收到通知并處理該消息。


聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報(bào)投訴
  • 存儲
    +關(guān)注

    關(guān)注

    13

    文章

    4226

    瀏覽量

    85576
  • 軟件設(shè)計(jì)
    +關(guān)注

    關(guān)注

    3

    文章

    58

    瀏覽量

    17753
  • 代碼
    +關(guān)注

    關(guān)注

    30

    文章

    4722

    瀏覽量

    68231
  • 模式
    +關(guān)注

    關(guān)注

    0

    文章

    65

    瀏覽量

    13365

原文標(biāo)題:觀察者模式,超詳細(xì)!

文章出處:【微信號:magedu-Linux,微信公眾號:馬哥Linux運(yùn)維】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    UVM設(shè)計(jì)模式觀察者模式解讀

    Observer Pattern:對象之間定義一個(gè)一對多的依賴關(guān)系,當(dāng)一個(gè)對象改變的時(shí)候,所有依賴對象都會自動收到通知。
    的頭像 發(fā)表于 08-24 17:39 ?1002次閱讀
    UVM設(shè)計(jì)<b class='flag-5'>模式</b>之<b class='flag-5'>觀察者</b><b class='flag-5'>模式</b>解讀

    如何觀察重復(fù)的公告?

    我能夠建立和運(yùn)行“第10天”觀察員樣本項(xiàng)目。我在另一個(gè)DeV板上運(yùn)行了一個(gè)信標(biāo),當(dāng)我啟動觀察者時(shí),它報(bào)告了“不可連接的單向廣告”,但是沒有新的事件,盡管我知道信標(biāo)必須定期發(fā)布一個(gè)廣告。作為證據(jù),如果
    發(fā)表于 10-14 07:06

    CC2540廣播角色和觀察者角色切換代碼怎么編寫?

    希望一個(gè)CC2540先通過觀察者角色獲取其他廣播的廣播數(shù)據(jù),然后在切換為廣播角色將這些數(shù)據(jù)廣播給另外一個(gè)觀察者?這樣就需要編程實(shí)現(xiàn)觀察者
    發(fā)表于 03-16 10:27

    RN4020觀察者模式無法正常工作怎么回事

    中心,支持MLDP,并使UART流控制R,1//重新引導(dǎo),使更改生效J,1//觀察者模式你對這個(gè)問題有什么想法?謝謝,弗朗西斯科
    發(fā)表于 04-22 09:03

    屬性觀察者的特點(diǎn)

    屬性觀察者,類似于觸發(fā)器。用來監(jiān)視屬性的除初始化之外的屬性值變化,當(dāng)屬性值發(fā)生改變時(shí)可以對此作出響應(yīng)。有如下特點(diǎn): 1,不僅可以在屬性值改變后觸發(fā)didSet,也可以在屬性值改變前觸發(fā)willSet
    發(fā)表于 11-04 07:10

    觀察者模式在嵌入式編程設(shè)計(jì)中有何作用

    觀察者模式是最常見的模式之一。這種模式提供一種方法來時(shí)對象“監(jiān)聽”其他對象,而不需要修改任何數(shù)據(jù)服務(wù)器。在嵌入式領(lǐng)域,這意味著數(shù)據(jù)能夠很容易分享給其他元素。
    發(fā)表于 12-22 08:31

    基于觀察者模式的屏幕布局控件設(shè)計(jì)

    觀察者模式作為設(shè)計(jì)模式中行為模式的一種,解決了上述具有一對多依賴關(guān)系對象重用問題。文中在分析觀察者模式
    發(fā)表于 02-13 16:20 ?4次下載
    基于<b class='flag-5'>觀察者</b><b class='flag-5'>模式</b>的屏幕布局控件設(shè)計(jì)

    Java設(shè)計(jì)模式分析之觀察者

    觀察者模式的流程跟報(bào)紙訂閱方式一致,即:觀察者模式=出版+訂閱,只是名稱不一樣,出版
    發(fā)表于 09-26 17:36 ?0次下載

    在 Java8 環(huán)境下實(shí)現(xiàn)觀察者模式的實(shí)例分析

    觀察者(Observer)模式又名發(fā)布-訂閱(Publish/Subscribe)模式,是四人組(GoF,即 Erich Gamma、Richard Helm、Ralph Johnson
    發(fā)表于 10-12 16:09 ?0次下載
    在 Java8 環(huán)境下實(shí)現(xiàn)<b class='flag-5'>觀察者</b><b class='flag-5'>模式</b>的實(shí)例分析

    大陸正在研發(fā)一款路況觀察者應(yīng)用

    據(jù)外媒報(bào)道,大陸正在研發(fā)一款路況觀察者(Road Condition Observer)應(yīng)用。隨著傳感器及攝像頭技術(shù)的愈發(fā)成熟,再結(jié)合車載流媒體數(shù)據(jù),或許能夠完成該應(yīng)用的設(shè)計(jì)。此外,工程師還將為該應(yīng)用寫入成熟的算法。
    發(fā)表于 05-15 11:30 ?993次閱讀

    GoF設(shè)計(jì)模式觀察者模式

    現(xiàn)在有 2 個(gè)服務(wù),Service A 和 Service B,通過 REST 接口通信;Service A 在某個(gè)業(yè)務(wù)場景下調(diào)用 Service B 的接口完成一個(gè)計(jì)算密集型任務(wù),假設(shè)接口為 http://service_b/api/v1/domain;該任務(wù)運(yùn)行時(shí)間很長,但 Service A 不想一直阻塞在接口調(diào)用上。為了滿足 Service A 的要求,通常有 2 種方案:
    的頭像 發(fā)表于 07-25 11:32 ?989次閱讀

    設(shè)計(jì)模式行為型:觀察者模式

    定義對象之間的一種一對多依賴關(guān)系,使得每一個(gè)對象發(fā)生狀態(tài)的變化時(shí),其相關(guān)依賴對象皆得到通知并被自動更新,又稱為發(fā)布-訂閱模式、模型-視圖模式、源-監(jiān)聽器模式或從屬
    的頭像 發(fā)表于 06-07 16:56 ?636次閱讀
    設(shè)計(jì)<b class='flag-5'>模式</b>行為型:<b class='flag-5'>觀察者</b><b class='flag-5'>模式</b>

    基于觀察者模式設(shè)計(jì)的框架-REB,使代碼模塊化

    設(shè)計(jì)模式里面的觀察者模式,一直是作者想去設(shè)計(jì)一套框架來闡述這一個(gè)模式,因此REB(Rice Event Broker)就是為了完成觀察者
    的頭像 發(fā)表于 10-17 09:35 ?620次閱讀
    基于<b class='flag-5'>觀察者</b><b class='flag-5'>模式</b>設(shè)計(jì)的框架-REB,使代碼模塊化

    一文解析BLE觀察者模式回調(diào)機(jī)制

    nRF5 SDK從版本14開始,對事件回調(diào)機(jī)制做了更新,引入了觀察者模式,以解耦不同BLE Layer對BLE事件的回調(diào)函數(shù)。
    的頭像 發(fā)表于 11-27 10:07 ?904次閱讀
    一文解析BLE<b class='flag-5'>觀察者</b><b class='flag-5'>模式</b>回調(diào)機(jī)制

    什么是觀察者設(shè)計(jì)模式?Golang中的觀察者模式介紹

    當(dāng)涉及到訂單處理系統(tǒng)時(shí),觀察者設(shè)計(jì)模式可以用于實(shí)現(xiàn)訂單狀態(tài)的變化和通知。
    的頭像 發(fā)表于 01-08 10:08 ?392次閱讀