1. 程式人生 > >觀察者模式與釋出/訂閱模式

觀察者模式與釋出/訂閱模式

觀察者模式本質上是一種物件行為模式,而 釋出/訂閱模式本質上是一種架構模式,強調元件的作用。

1. 觀察者模式

觀察者模式是一種設計模式,其中一個物件(稱為主體)根據物件(觀察者)維護一個物件列表,自動通知他們對狀態的任何更改。

意圖:定義物件間的一種一對多的依賴關係,當一個物件的狀態發生改變時,所有依賴於它的物件都得到通知並被自動更新。
動機:將一個系統分割成一系列相互協作的類有一個副作用:需要維護相關物件間的一致性。不希望為了維持一致性而使各類緊密耦合,這樣會降低可重用性。

“一個或多個觀察者對某個主體的狀態感興趣,並通過附加他們自己的興趣來註冊主題,當觀察者感興趣的主題發生變化時,會發送通知訊息,在每個主題中呼叫更新方法觀察者,當觀察者不再對主體的狀態感興趣時,他們可以簡單地分離自己。“

可能從上面的描述還不能都抓到重要的資訊。接下來我們還是先看一下design pattern書中的例子:

<!DOCTYPE html>
<html>
  <head>
    <title>The "Click the button" page</title>
    <meta charset="UTF-8">
  </head>
  <body>
    <button id="addNewObserver">Add New Observer checkbox</button>
    <input id="mainCheckbox" type="checkbox"/>
    <div id="observersContainer"></div>
    <script type="text/javascript" src='index.js'></script>
  </body>
</html>
/*
 * Subject
 * 內部建立了三個方法,內部維護了一個ObserverList。
 */


//contructor function
function Subject(){
  this.observers = new ObserverList();
}
 
//addObserver: 呼叫內部維護的ObserverList的add方法
Subject.prototype.addObserver = function( observer ){
  this.observers.add( observer );
};
 
//removeObserver: 呼叫內部維護的ObserverList的removeat方法
Subject.prototype.removeObserver = function( observer ){
  this.observers.removeAt( this.observers.indexOf( observer, 0 ) );
};
 
//notify: 通知函式,用於通知觀察者並且執行update函式,update是一個實現介面的方法,是一個通知的觸發方法。
Subject.prototype.notify = function( context ){
  var observerCount = this.observers.count();
  for(var i=0; i < observerCount; i++){
    this.observers.get(i).update( context );
  }
};

/*
 * ObserverList
 * 內部維護了一個數組,4個方法用於陣列的操作,這裡相關的內容還是屬於subject,因為ObserverList的存在是為了將subject和內部維護的observers分離開來,清晰明瞭的作用。
 */
function ObserverList(){
  this.observerList = [];
}
 
ObserverList.prototype.add = function( obj ){
  return this.observerList.push( obj );
};
 
ObserverList.prototype.count = function(){
  return this.observerList.length;
};
 
ObserverList.prototype.get = function( index ){
  if( index > -1 && index < this.observerList.length ){
    return this.observerList[ index ];
  }
};
 
ObserverList.prototype.indexOf = function( obj, startIndex ){
  var i = startIndex;
 
  while( i < this.observerList.length ){
    if( this.observerList[i] === obj ){
      return i;
    }
    i++;
  }
 
  return -1;
};
 
ObserverList.prototype.removeAt = function( index ){
  this.observerList.splice( index, 1 );
};

/*
 * The Observer
 * 提供更新介面,為想要得到通知訊息的主體提供介面。
 */ 
function Observer(){
  this.update = function(){
    // ...
  };
}


// Extend an object with an extension
function extend( obj, extension ){
  for ( var key in extension ){
    obj[key] = extension[key];
  }
}
 
// References to our DOM elements
 
var controlCheckbox = document.getElementById( "mainCheckbox" ),
  addBtn = document.getElementById( "addNewObserver" ),
  container = document.getElementById( "observersContainer" );
 
 
// Concrete Subject
 
// Extend the controlling checkbox with the Subject class
extend( controlCheckbox, new Subject() );
 
// Clicking the checkbox will trigger notifications to its observers
controlCheckbox.onclick = function(){
  controlCheckbox.notify( controlCheckbox.checked );
};
 
addBtn.onclick = addNewObserver;
 
// Concrete Observer
function addNewObserver(){
  // Create a new checkbox to be added
  var check = document.createElement( "input" );
  check.type = "checkbox";
 
  // Extend the checkbox with the Observer class
  extend( check, new Observer() );
 
  // Override with custom update behaviour
  check.update = function( value ){
    this.checked = value;
  };
 
  // Add the new observer to our list of observers
  // for our main subject
  controlCheckbox.addObserver( check );
 
  // Append the item to the container
  container.appendChild( check );
}

上面的例子其實分為了下面的幾個部分:
Subject:維護觀察員名單,便於新增或刪除觀察員
Observer:為需要被通知主體的狀態改變的物件提供更新介面
ConcreteSubject:向觀察者廣播關於狀態變化的通知,儲存ConcreteObservers的狀態
ConcreteObserver:儲存對ConcreteSubject的引用,實現Observer的更新介面,以確保狀態與Subject的一致

然後我們將上面的各個部分對應到例子中去:

具體的操作流程如下

a. 定義好主體類和觀察者類
b. 類例項化成具體物件,繫結方法,觀察者在主體物件裡註冊
c. 操作。主體分發訊息給觀察者。


走到這裡,看到的是一種什麼行為呢?觀察者模式自己已經定義好觀察者和主題的結構,然後由具體的主題和觀察者去繼承它,這也是為什麼上面說的是一種物件行為模式,在定義的Subject和Observer之間已經定義好了彼此之間的聯絡,這是一個緊耦合的狀態。觀察者為什麼這麼做呢,從觀察者模式自己的名稱來看,它的側重點就是觀察物件,觀察者的狀態需要隨著主題的通知去發生變化,所以自己本身定義了主題和觀察者之間的緊密聯絡。

2.釋出/訂閱模式

釋出/訂閱模式使用位於希望接收通知的物件(訂閱者)和發起事件的物件(釋出者)之間的主題/事件頻道。這個事件系統允許程式碼定義特定於應用程式的事件,這些事件可以傳遞包含訂戶所需值的自定義引數 這裡的想法是避免使用者和釋出者之間的依賴關係。這裡和觀察者模式就不一樣了。側重點在於訂閱者訂閱事件,釋出者釋出資訊,至於訂閱者接受資訊之後的處理並不關心。但是這個地方的問題在於Subject已經是專案的一個實實在在的構成部分,它不再是一個去規範行為的東西。這也是為什麼一開始說是一種架構模式。

var pubsub = {};
 
(function(myObject) {
 
    // Storage for topics that can be broadcast
    // or listened to
    var topics = {};
 
    // A topic identifier
    var subUid = -1;
 
    // Publish or broadcast events of interest
    // with a specific topic name and arguments
    // such as the data to pass along
    myObject.publish = function( topic, args ) {
 
        if ( !topics[topic] ) {
            return false;
        }
 
        var subscribers = topics[topic],
            len = subscribers ? subscribers.length : 0;
 
        while (len--) {
            subscribers[len].func( topic, args );
        }
 
        return this;
    };
 
    // Subscribe to events of interest
    // with a specific topic name and a
    // callback function, to be executed
    // when the topic/event is observed
    myObject.subscribe = function( topic, func ) {
 
        if (!topics[topic]) {
            topics[topic] = [];
        }
 
        var token = ( ++subUid ).toString();
        topics[topic].push({
            token: token,
            func: func
        });
        return token;
    };
 
    // Unsubscribe from a specific
    // topic, based on a tokenized reference
    // to the subscription
    myObject.unsubscribe = function( token ) {
        for ( var m in topics ) {
            if ( topics[m] ) {
                for ( var i = 0, j = topics[m].length; i < j; i++ ) {
                    if ( topics[m][i].token === token ) {
                        topics[m].splice( i, 1 );
                        return token;
                    }
                }
            }
        }
        return this;
    };
}( pubsub ));

// A simple message logger that logs any topics and data received through our
// subscriber
var messageLogger = function ( topics, data ) {
    console.log( "Logging: " + topics + ": " + data );
};

 
// Subscribers listen for topics they have subscribed to and
// invoke a callback function (e.g messageLogger) once a new
// notification is broadcast on that topic
var subscription = pubsub.subscribe( "inbox/newMessage", messageLogger );
 
// Publishers are in charge of publishing topics or notifications of
// interest to the application. e.g:
 
pubsub.publish( "inbox/newMessage", "hello world!" );
 
// or
pubsub.publish( "inbox/newMessage", ["test", "a", "b", "c"] );
 
// or
pubsub.publish( "inbox/newMessage", {
  sender: "[email protected]",
  body: "Hey again!"
});
 
// We can also unsubscribe if we no longer wish for our subscribers
// to be notified
pubsub.unsubscribe( subscription );
 
// Once unsubscribed, this for example won't result in our
// messageLogger being executed as the subscriber is
// no longer listening
pubsub.publish( "inbox/newMessage", "Hello! are you still there?" );

3. 兩者之間的區別

主題與訂閱者之間的聯絡

觀察者模式是一種緊耦合的狀態,而釋出/訂閱模式是一種鬆耦合的狀態。

通知訂閱者的方式

觀察者模式是通過主題自己本身去遍歷觀察者,然後呼叫訂閱者的通知方法去實現的。而釋出/訂閱模式是通過事件管道去通知的,其實做這個事情的主題是是事件,因為在執行具體的事件的時候,沒人知道接下來執行的方法是什麼嗎?因為訂閱/釋出模式維護了所有的訂閱者事件。其實二者之間就好像一個是授之以漁,另外一個是授之以魚。

觀察者模式告訴我們接下來程式碼執行的步驟,而訂閱/釋出模式就好像是自己親自上陣將所有的事情搞好。

內部維護的內容

觀察者模式維護了觀察者,知道有哪些觀察者在關注。訂閱/釋出模式則省略了這一步驟,直接維護訂閱者的事件機制,直接執行具體的事件好了,至於是誰在訂閱,管他的呢!

舉個例子

中心是我自己,我做了兩件事情,一是買了股票。二是訂閱了報紙。每一天這兩件事情都會發生。但是作為我來說,我的想法是什麼呢?股票開市了,這一天忙的一直盯著螢幕,生怕別跌了。對於股票這件事情,股票是主題,而我是訂閱者,我們之間的關係在我看來非常重要,關係著我的錢,能不緊密嗎。可是今天的行情不太好,跌了。回到家心情很低落,回到家待了一會,想起今天的報紙,沒有送來,於是就打電話問一下是怎麼回事?原來已經送來了,被室友領過了。於是我知道報紙送來了就行了,心情不好就不看了。對於報紙這件事來說,我關心的是今天的報紙印了,並且也送來了就行,不看就不看了。

參考:

https://addyosmani.com/resources/essentialjsdesignpatterns/book/#observerpatternjavascript

http://blog.csdn.net/elibrace/article/details/37601001

https://segmentfault.com/a/1190000013534768?utm_source=index-hottest

相關推薦

觀察模式釋出/訂閱模式區別

在翻閱資料的時候,有人把觀察者(Observer)模式等同於釋出(Publish)/訂閱(Subscribe)模式,也有人認為這兩種模式還是存在差異,而我認為確實是存在差異的,本質上的區別是排程的地方不同。 觀察者模式 比較概念的解釋是,目標和觀察者是基類,目標提供維護觀察

觀察模式釋出/訂閱模式的區別

        不管是維基百科還是百度百科,搜尋觀察者模式,都會發現觀察者模式的定義是觀察者模式(有時又被稱為釋出/訂閱模式),即一個目標物件管理所有相依於它的觀察者物件,並且在它本身的狀態改變時主動發出通知。這通常透過呼叫各觀察者所提供的方法來實現。此種模式通常被用來實

觀察模式釋出/訂閱模式

觀察者模式本質上是一種物件行為模式,而 釋出/訂閱模式本質上是一種架構模式,強調元件的作用。 1. 觀察者模式 觀察者模式是一種設計模式,其中一個物件(稱為主體)根據物件(觀察者)維護一個物件列表,自動通知他們對狀態的任何更改。 意圖:定義物件間的一種一對多的依賴關係,

觀察模式 vs 釋出訂閱模式

原文地址:zhuanlan.zhihu.com/p/51357583 作者:柳樹 有一回面試,面試官問: 觀察者模式,和釋出訂閱模式,有什麼區別? 我腦海中立刻閃現了《Head First設計模式》裡講的: Publishers + Subscribers = Observe

觀察模式釋出/訂閱模式

  觀察者模式提供了避免元件之間緊密耦合的另一種方法,它將觀察者和被觀察者的物件分離開。在該模式中,一個物件通過新增一個方法(該方法允許另一個物件,即觀察者註冊自己)使本身變得可觀察。當可觀察的物件更改時,它會將訊息傳送到已註冊的觀察者。這些觀察者收到訊息後所執行的操作與可觀

觀察模式(Observer Pattern,物件行為型模式釋出-訂閱模式 Publish/Subscribe Pattern)

意圖 通知狀態變化 定義物件間的一對多的依賴關係,當一個物件的狀態發生變化時,所有依賴它的物件都得到通知並被自動更新,由抽象類或介面實現。 推模式:目標角色複雜,並且觀察者角色進行更行時必須得到一些具體的變化資訊 拉模式:目標角色簡單 適用性 在

Java設計模式觀察模式釋出/訂閱模式

1、概述 觀察者模式又稱為釋出/訂閱(Publish/Subscribe)模式 觀察者設計模式涉及到兩種角色:主題(Subject)和觀察者(Observer) (1)Subject模組 Sub

觀察模式釋出訂閱模式

1.觀察者模式的背後,總的想法是在應用程式中增強鬆耦合性。 2.觀察者也被叫做訂閱者,它指向被觀察的物件,即被觀察者。當事件發生時,被觀察者就會通知觀察者。 3.觀察者的使用場合 當一個物件的改變需要同時改變其他物件,並且它不知道有多少物件需要改變的時候。

觀察模式釋出訂閱模式的區別

之前一直對觀察者模式和釋出訂閱模式的區別理解不深,正好這段時間在看vue原始碼的分析,vue資料雙向繫結也用到了釋出訂閱模式,於是

訊息佇列中點對點模型釋出/訂閱模式的區別

背景知識 JMS一個在 Java標準化組織(JCP)內開發的標準(代號JSR 914)。2001年6月25日,Java訊息服務釋出JMS 1.0.2b,2002年3月18日Java訊息服務釋出 1.1.  Java訊息服務(Java Message Service,JMS)

23種設計模式 之 Observer模式釋出-訂閱模式) C語言

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

vue非父子元件間的傳值 Bus/匯流排機制/釋出訂閱模式/觀察模式

Bus/匯流排機制/釋出訂閱模式/觀察者模式 我們需要將一個元件的子元件的資料傳遞給另一個元件的子元件 ① 將Vue的例項賦值給Vue.prototype.bus,這樣只要我們之後呼叫new Vue()或者建立元件的時候,每一個元件上都會有Bus這個屬性,因為每一個元件或者Vue這個例項

那些年 你需要解決的觀察模式/釋出-訂閱模式(ES6語法)

在筆試和一些面試經驗交流的帖子中程序出現訂閱者模式,想著自己還不是很清楚,所以得解決,但是我們的目的應該不僅僅是對付筆試和麵試,應該瞭解這個模式的思想:鬆散耦合程式碼形式!更多的大家看別的描述吧,會比我更清楚,這裡也不累述,只是加入自己更加通俗的理解:我們的_events

觀察釋出訂閱模式的區別

       Pub-Sub  Pattern          在“釋出者-訂閱者”模式中,稱為釋出者的訊息傳送者不會將訊息程式設計為直接傳送給稱為訂閱者的特定接收者。這意味著釋出者和訂閱者不知道彼此的存在。存在第三個元件,稱為代理或訊息代理或事件匯流排,它由釋出者和訂閱者都知道,它過濾所有傳入的訊息並相

設計模式——觀察釋出訂閱模式

最近在學習設計模式,本文就同一個例子對觀察者和釋出訂閱進行探討。觀察者模式    比較概念的解釋是,目標和觀察者是基類,目標提供維護觀察者的一系列方法,觀察者提供更新介面。具體觀察者和具體目標繼承各自的基類,然後具體觀察者把自己註冊到具體目標裡,在具體目標發生變化時候,排程觀

Java中的設計模式 - 觀察模式【又叫:釋出/訂閱模式

文章目錄 Java中的設計模式 - 觀察者模式【又叫:釋出/訂閱模式】 1、觀察者模式是為了解決什麼問題 2、核心邏輯 3、優點 4、缺點 5、應用場景

設計模式觀察模式(釋出/訂閱模式:publish/subscribe)

定義了一種一對多的依賴關係,讓多個觀察者物件同時監聽某一個主題物件.這個主題物件在狀態發生變化時,會通知所有觀察者物件,使它們能夠自動更新自己 觀察者模式的關鍵物件是主題Subject和觀察者Observer.一個Subject可以有任意數目依賴它的Observer, 一

設計模式--觀察模式(釋出訂閱模式)

類圖 定義 定義物件間一種一對多的依賴關係,使得每當一個物件改變狀態,則所有依賴於它的物件都會得到通知並被自動更新 優點 - 觀察者和被觀察

SpringBoot事件監聽機制及觀察模式/釋出訂閱模式

[toc] ## 本篇要點 - 介紹觀察者模式和釋出訂閱模式的區別。 - SpringBoot快速入門事件監聽。 ## 什麼是觀察者模式? 觀察者模式是經典行為型設計模式之一。 在GoF的《設計模式》中,觀察者模式的定義:**在物件之間定義一個一對多的依賴,當一個物件狀態改變的時候,所有依賴的物件都會自

訊息佇列模式:點對點 釋出訂閱

Java訊息服務(Java Message Service,JMS)應用程式介面是一個Java平臺中關於面向訊息中介軟體(MOM)的API,用於在兩個應用程式之間,或分散式系統中傳送訊息,進行非同步通訊。 點對點與釋出訂閱最初是由JMS定義的。這兩種模式主要區別或解決的問題