最近學習了觀察者模式和發佈/訂閱模式,可是一直有種不得要領的感受,今天從新複習了一遍又有了新的思考,記錄一下學習收穫。javascript
概念引用原文的話以下:vue
The Observer is a design pattern where an object (known as a subject) maintains a list of objects depending on it (observers), automatically notifying them of any changes to state.java
一個對象(subject)維護一個對象列表(observers),當發生任何狀態改變時自動通知它們設計模式
根據概念畫了一個大概的關係圖bash
接下來直接看看代碼的實現框架
先是一個observeList類,這個類有幾個維護observerList的方法,這幾個方法是用來管理對象維護的觀察者列表的方法(相似增刪改查)。ide
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 );
};
複製代碼
接下來是subject類,用於給某個具體的被觀察的對象繼承,這個類有包裝了新增觀察者(addObserver)、移除觀察者(removeObserver)以及通知觀察者(notify)的方法。學習
function Subject(){
this.observers = new ObserverList();
}
Subject.prototype.addObserver = function( observer ){
this.observers.add( observer );
};
Subject.prototype.removeObserver = function( observer ){
this.observers.removeAt( this.observers.indexOf( observer, 0 ) );
};
Subject.prototype.notify = function( context ){
var observerCount = this.observers.count();
for(var i=0; i < observerCount; i++){
this.observers.get(i).update( context );
}
};
複製代碼
最後是一個觀察者的類,這個其實並非強制要求以這個類來實現,只是說明了觀察者須要暴露一個update方法,當subject的實例發生變化時,subject實例使用notify方法去調用觀察者的update方法。ui
function Observer(){
this.update = function(){
// ...
};
}
複製代碼
剛開始看觀察者模式的時候一直不是很明白這個設計在開發中的應用,今天又從新看的時候突然想起來和以前看vue的雙向綁定一部分的實現很像。在雙向綁定裏當咱們改變一個對象的值的時候,其餘使用了這個值的地方也會對應改變,這和觀察者模式的設計是比較相似的。this
發佈/訂閱模式和觀察者模式有點相似,可是發佈/訂閱模式不存在觀察者與被觀察者的關係,這種設計模式相對於觀察者模式更加的鬆耦合,實現代碼以下:
一個對象若是提供了支持Publish()、Subscribe()和unsubscribe()的功能實現,就可使用Publish/Subscribe。
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 ));
複製代碼
發佈/訂閱模式和vue的eventBus殊途同歸,在實際應用中,由於任何地方咱們均可以訂閱(subscribe)一個事件,也能夠在任何地方發佈(public)一個事件,因此在這個設計模式中,誰發佈或者誰訂閱了事件是沒法感知的,這也是和觀察者模式最大的區別。
剛開始學習觀察者模式和發佈/訂閱模式真的是徹底沒有什麼感受,簡單的說的就是代碼都看懂了,可是不知道有什麼用。今天從新看了一遍,仔細想了一下設計的用法,和實際開發中對應起來就特別有感受了,學習設計模式對於咱們閱讀源碼和開發中體會框架的設計理念很是有幫助。