## 介紹
職責鏈模式(Chain of responsibility)是使多個對象都有機會處理請求,從而避免請求的發送者和接受者之間的耦合關系。將這個對象連成一條鏈,并沿著這條鏈傳遞該請求,直到有一個對象處理他為止。
也就是說,請求以后,從第一個對象開始,鏈中收到請求的對象要么親自處理它,要么轉發給鏈中的下一個候選者。提交請求的對象并不明確知道哪一個對象將會處理它——也就是該請求有一個隱式的接受者(implicit receiver)。根據運行時刻,任一候選者都可以響應相應的請求,候選者的數目是任意的,你可以在運行時刻決定哪些候選者參與到鏈中。
## 正文
對于JavaScript實現,我們可以利用其原型特性來實現職責鏈模式。
~~~
var NO_TOPIC = -1;
var Topic;
function Handler(s, t) {
this.successor = s || null;
this.topic = t || 0;
}
Handler.prototype = {
handle: function () {
if (this.successor) {
this.successor.handle()
}
},
has: function () {
return this.topic != NO_TOPIC;
}
};
~~~
Handler只是接受2個參數,第一個是繼任者(用于將處理請求傳下去),第二個是傳遞層級(可以用于控制在某個層級下是否執行某個操作,也可以不用),Handler原型暴露了一個handle方法,這是實現該模式的重點,先來看看如何使用上述代碼。
~~~
var app = new Handler({
handle: function () {
console.log('app handle');
}
}, 3);
var dialog = new Handler(app, 1);
var button = new Handler(dialog, 2);
button.handle();
~~~
改代碼通過原型特性,調用代碼從button.handle()->dialog.handle()->app.handle()->參數里的handle(),前三個都是調用原型的handle,最后才查找到傳入的參數里的handle,然后輸出結果,也就是說其實只有最后一層才處理。
那如何做到調用的時候,只讓dialog的這個對象進行處理呢?其實可以定義dialog實例對象的handle方法就可以了,但需要在new button的之前來做,代碼如下:
~~~
var app = new Handler({
handle: function () {
console.log('app handle');
}
}, 3);
var dialog = new Handler(app, 1);
dialog.handle = function () {
console.log('dialog before ...')
// 這里做具體的處理操作
console.log('dialog after ...')
};
var button = new Handler(dialog, 2);
button.handle();
~~~
該代碼的執行結果即時dialog.handle里的處理結果,而不再是給app傳入的參數里定義的handle的執行操作。
那能不能做到自身處理完以后,然后在讓繼任者繼續處理呢?答案是肯定的,但是在調用的handle以后,需要利用原型的特性調用如下代碼:
~~~
Handler.prototype.handle.call(this);
~~~
該句話的意思說,調用原型的handle方法,來繼續調用其繼任者(也就是successor )的handle方法,以下代碼表現為:button/dialog/app三個對象定義的handle都會執行。
~~~
var app = new Handler({
handle: function () {
console.log('app handle');
}
}, 3);
var dialog = new Handler(app, 1);
dialog.handle = function () {
console.log('dialog before ...')
// 這里做具體的處理操作
Handler.prototype.handle.call(this); //繼續往上走
console.log('dialog after ...')
};
var button = new Handler(dialog, 2);
button.handle = function () {
console.log('button before ...')
// 這里做具體的處理操作
Handler.prototype.handle.call(this);
console.log('button after ...')
};
button.handle();
~~~
通過代碼的運行結果我們可以看出,如果想先自身處理,然后再調用繼任者處理的話,就在末尾執行Handler.prototype.handle.call(this);代碼,如果想先處理繼任者的代碼,就在開頭執行Handler.prototype.handle.call(this);代碼。
## 總結
職責鏈模式經常和組合模式一起使用,這樣一個構件的父構件可以作為其繼任者。
同時,DOM里的事件冒泡機制也和此好像有點類似,比如點擊一個按鈕以后,如果不阻止冒泡,其click事件將一直向父元素冒泡,利用這個機制也可以處理很多相關的問題,比如本系列設計模式享元模式里的《例1:事件集中管理》的示例代碼。
參考代碼:https://gist.github.com/1174982
- (1)編寫高質量JavaScript代碼的基本要點
- (2)揭秘命名函數表達式
- (3)全面解析Module模式
- (4)立即調用的函數表達式
- (5)強大的原型和原型鏈
- (6)S.O.L.I.D五大原則之單一職責SRP
- (7)S.O.L.I.D五大原則之開閉原則OCP
- (8)S.O.L.I.D五大原則之里氏替換原則LSP
- (9)根本沒有“JSON對象”這回事!
- (10)JavaScript核心(晉級高手必讀篇)
- (11)執行上下文(Execution Contexts)
- (12)變量對象(Variable Object)
- (13)This? Yes, this!
- (14)作用域鏈(Scope Chain)
- (15)函數(Functions)
- (16)閉包(Closures)
- (17)面向對象編程之一般理論
- (18)面向對象編程之ECMAScript實現
- (19)求值策略
- (20)《你真懂JavaScript嗎?》答案詳解
- (21)S.O.L.I.D五大原則之接口隔離原則ISP
- (22)S.O.L.I.D五大原則之依賴倒置原則DIP
- (23)JavaScript與DOM(上)——也適用于新手
- (24)JavaScript與DOM(下)
- (25)設計模式之單例模式
- (26)設計模式之構造函數模式
- (27)設計模式之建造者模式
- (28)設計模式之工廠模式
- (29)設計模式之裝飾者模式
- (30)設計模式之外觀模式
- (31)設計模式之代理模式
- (32)設計模式之觀察者模式
- (33)設計模式之策略模式
- (34)設計模式之命令模式
- (35)設計模式之迭代器模式
- (36)設計模式之中介者模式
- (37)設計模式之享元模式
- (38)設計模式之職責鏈模式
- (39)設計模式之適配器模式
- (40)設計模式之組合模式
- (41)設計模式之模板方法
- (42)設計模式之原型模式
- (43)設計模式之狀態模式
- (44)設計模式之橋接模式
- (45)代碼復用模式(避免篇)
- (46)代碼復用模式(推薦篇)
- (47)對象創建模式(上篇)
- (48)對象創建模式(下篇)
- (49)Function模式(上篇)
- (50)Function模式(下篇)
- (結局篇)