摘要:使用面向切面編程來快速的創(chuàng)建職責(zé)鏈的具體概念可以參考裝飾者模式實(shí)現(xiàn)職責(zé)鏈簡單又巧妙,但這種把函數(shù)疊在一起的方式,同時(shí)也疊加了函數(shù)的作用域,如果鏈條太長的話,也會(huì)對性能造成太大的影響。在開發(fā)中,職責(zé)鏈模式是最容易被忽視的模式之一。
聲明:這個(gè)系列為閱讀《JavaScript設(shè)計(jì)模式與開發(fā)實(shí)踐》 ----曾探@著一書的讀書筆記
1.職責(zé)鏈模式的定義
2.
2.1 簡單職責(zé)鏈模式
2.2職責(zé)鏈重構(gòu)上面的代碼
2.3靈活的拆分職責(zé)鏈節(jié)點(diǎn)
3.異步職責(zé)鏈
4.職責(zé)鏈模式的優(yōu)缺點(diǎn):
5.使用AOP(面向切面編程)來快速的創(chuàng)建職責(zé)鏈
總結(jié):
1.職責(zé)鏈模式的定義2. 2.1 簡單職責(zé)鏈模式使多個(gè)對象都有機(jī)會(huì)處理請求,從而避免請求的發(fā)送者和接收者之間的耦合關(guān)系,將這些對象連成一條鏈,并沿著這條鏈傳遞該請求,直到有一個(gè)對象處理它為止。
故事背景:用戶可以支付定金購買手機(jī),并且可以獲得優(yōu)惠券。沒有支付定金的就是普通用戶,進(jìn)入普通購買模式,沒有優(yōu)惠券,且?guī)齑娌蛔愕那闆r下不一定能夠買到手機(jī)。
/** * * @param orderType 訂單類型 * @param pay 用戶是否已經(jīng)支付過定金 true or false * @param stock 表示手機(jī)的庫存量 */ var order = function (orderType, pay, stock){ if (orderType === 1) { if (pay === true) { console.log("500定金預(yù)購,得到100元優(yōu)惠券"); } else { if (stock > 0) { console.log("普通購買,沒有優(yōu)惠券"); } else { console.log("手機(jī)庫存不足"); } } } else if (orderType === 2) { if (pay === true) { console.log("200定金預(yù)購,得到50元優(yōu)惠券"); } else { if (stock > 0) { console.log("普通購買,沒有優(yōu)惠券"); } else { console.log("手機(jī)庫存不足"); } } } else if (orderType === 3) { if (stock > 0) { console.log("普通購買,沒有優(yōu)惠券"); } else { console.log("手機(jī)庫存不足"); } } };2.2職責(zé)鏈重構(gòu)上面的代碼
主要通過拆分功能語句,來使用職責(zé)鏈重構(gòu):
//500元訂單 var order500 = function (orderType, pay, stock){ if (orderType === 1 && pay === true) { console.log("500定金預(yù)購,得到100元優(yōu)惠券"); } else { order200(orderType, pay, stock); //將請求傳遞給200 } }; //200元訂單 var order200 = function (orderType, pay, stock){ if (orderType === 2 && pay === true) { console.log("200定金預(yù)購,得到50元優(yōu)惠券"); } else { order(orderType, pay, stock); } }; //普通購買訂單 var order = function (orderType, pay, stock){ if (stock>0) { console.log("普通購買,沒有優(yōu)惠券"); } else { console.log("手機(jī)庫存不足"); } }; //測試調(diào)用 order500(1,true,500); order500(3,false,0);
總結(jié):
上面的代碼違反了開放-封閉的原則,請求在鏈條中傳遞的順序非常僵硬,傳遞請求的代碼被耦合在了業(yè)務(wù)函數(shù)中:
var order500 = function (orderType, pay, stock){ if (orderType === 1 && pay === true) { console.log("500定金預(yù)購,得到100元優(yōu)惠券"); } else { order200(orderType, pay, stock); //將請求傳遞給200 } };2.3靈活的拆分職責(zé)鏈節(jié)點(diǎn)
為什么要拆分職責(zé)鏈的節(jié)點(diǎn),因?yàn)槟程煨枰砑有碌穆氊?zé),就需要修改業(yè)務(wù)代碼(要修改的話,就需要先去了解他,熟悉它,花費(fèi)大量的時(shí)間)。這顯然不是每一個(gè)人所需要的。
//500元訂單 var order500 = function (orderType, pay, stock){ if (orderType === 1 && pay === true) { console.log("500定金預(yù)購,得到100元優(yōu)惠券"); } else { return "nextSuccessor"; } }; //200元訂單 var order200 = function (orderType, pay, stock){ if (orderType === 2 && pay === true) { console.log("200定金預(yù)購,得到50元優(yōu)惠券"); } else { return "nextSuccessor"; } }; //普通購買訂單 var order = function (orderType, pay, stock){ if (stock>0) { console.log("普通購買,沒有優(yōu)惠券"); } else { console.log("手機(jī)庫存不足"); } }; var Chain=function (fn){ this.fn=fn; this.successor=null; }; Chain.prototype.setNextSuccessor=function (successor){ return this.successor=successor; }; Chain.prototype.passRequest=function(){ var ret= this.fn.apply(this,arguments); if(ret==="nextSuccessor"){ return this.successor && this.successor.passRequest.apply(this.successor,arguments); } return ret; }; var chainOrder500=new Chain(order500()); var chainOrder200=new Chain(order200()); var chainOrderNormal=new Chain(order()); chainOrder500.setNextSuccessor(chainOrder200); chainOrder200.setNextSuccessor(chainOrderNormal); chainOrder500.passRequest(1,true,500); chainOrder500.passRequest(2,true,500); chainOrder500.passRequest(1,false,0);
加入某天網(wǎng)站添加了300元定金購買的職責(zé),我只需要添加特定的節(jié)點(diǎn)就可以了:
//300元訂單 var order300=function (){ }; var chainOrder300=new Chain(order300()); chainOrder500.setNextSuccessor(chainOrder300); chainOrder500.setNextSuccessor(chainOrder200);
這樣的話只需要編寫簡單的功能函數(shù),改變職責(zé)鏈中的相關(guān)節(jié)點(diǎn)的順序即可。
3.異步職責(zé)鏈上面的職責(zé)鏈代碼中,每個(gè)節(jié)點(diǎn)函數(shù)同步返回一個(gè)特定的值nextSuccessor,來表示是否把請求傳遞給下一個(gè)節(jié)點(diǎn)。而現(xiàn)實(shí)開發(fā)中會(huì)遇到一些異步的問題,比如在一個(gè)節(jié)點(diǎn)中發(fā)起一個(gè)ajax異步請求,異步請求的結(jié)果才能決定是否繼續(xù)在職責(zé)鏈中passRequest。
可以給Chain類添加一個(gè)原型方法Chain.prototype.next,表示手動(dòng)傳遞請求給職責(zé)鏈中的下一個(gè)節(jié)點(diǎn):
Chain.prototype.next=function(){ return this.successor && this.successor.passRequest.apply(this.successor,arguments); }; //異步職責(zé)鏈的例子 var fn1=new Chain(function (){ console.log(1); return "nextSuccessor"; }); var fn2=new Chain(function (){ console.log(2); var self=this; setTimeout(function (){ self.next(); },1000); }); var fn3=new Chain(function (){ console.log(3); }); fn1.setNextSuccessor(fn2).setNextSuccessor(fn3); fn1.passRequest();4.職責(zé)鏈模式的優(yōu)缺點(diǎn):
優(yōu)點(diǎn):
職責(zé)鏈最大的優(yōu)點(diǎn)就是解耦了請求發(fā)送者和N個(gè)接收者之間的復(fù)雜關(guān)系。
職責(zé)鏈可以手動(dòng)指定起始節(jié)點(diǎn),請求并不是非得從鏈中的第一個(gè)節(jié)點(diǎn)開始傳遞。
缺點(diǎn):
不能保證某個(gè)請求一定會(huì)被鏈中的節(jié)點(diǎn)處理,這種情況可以在鏈尾增加一個(gè)保底的接受者節(jié)點(diǎn)來處理這種即將離開鏈尾的請求。
使程序中多了很多節(jié)點(diǎn)對象,可能再一次請求的過程中,大部分的節(jié)點(diǎn)并沒有起到實(shí)質(zhì)性的作用。他們的作用僅僅是讓請求傳遞下去,從性能當(dāng)面考慮,要避免過長的職責(zé)鏈到來的性能損耗。
5.使用AOP(面向切面編程)來快速的創(chuàng)建職責(zé)鏈AOP的具體概念可以參考裝飾者模式
Function.prototype.after=function(fn){ var self=this; return function(){ var ret=self.apply(this,arguments); if(ret==="nextSuccessor"){ return fn.apply(this,arguments); } return ret; } }; var order=order500yuan.after(order200yuan).after(orderNormal); order(1,true,500); order(1,false,500);
AOP實(shí)現(xiàn)職責(zé)鏈簡單又巧妙,但這種把函數(shù)疊在一起的方式,同時(shí)也疊加了函數(shù)的作用域,如果鏈條太長的話,也會(huì)對性能造成太大的影響。
總結(jié):職責(zé)鏈模式最大的優(yōu)點(diǎn):請求發(fā)送者只需要知道鏈中的第一個(gè)節(jié)點(diǎn),從而弱化了發(fā)送者和一組接收者之前的強(qiáng)聯(lián)系。
在JavaScript開發(fā)中,職責(zé)鏈模式是最容易被忽視的模式之一。但是只要運(yùn)用得當(dāng),職責(zé)鏈模式可以很好的幫助我們管理代碼,降低發(fā)起請求的對象和處理請求的對象之間的耦合性。且職責(zé)鏈中節(jié)點(diǎn)的數(shù)量和數(shù)序是可以自由變化的。可以在運(yùn)行時(shí)決定鏈中包含哪些節(jié)點(diǎn)。
無論是作用域鏈,原型鏈,還是DOM節(jié)點(diǎn)中的事件冒泡,我們都能從中找到職責(zé)鏈模式的影子。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/90839.html
摘要:代理模式,迭代器模式,單例模式,裝飾者模式最少知識(shí)原則一個(gè)軟件實(shí)體應(yīng)當(dāng)盡可能少地與其他實(shí)體發(fā)生相互作用。迭代器模式可以將迭代的過程從業(yè)務(wù)邏輯中分離出來,在使用迭代器模式之后,即不用關(guān)心對象內(nèi)部構(gòu)造也可以按順序訪問其中的每個(gè)元素。 接手項(xiàng)目越來越復(fù)雜的時(shí)候,有時(shí)寫完一段代碼,總感覺代碼還有優(yōu)化的空間,卻不知道從何處去下手。設(shè)計(jì)模式主要目的是提升代碼可擴(kuò)展性以及可閱讀性。 本文主要以例子的...
摘要:裝飾者模式定義裝飾者模式能夠在不改變對象自身的基礎(chǔ)上,在程序運(yùn)行期間給對像動(dòng)態(tài)的添加職責(zé)。與繼承相比,裝飾者是一種更輕便靈活的做法。 裝飾者模式 定義 : 裝飾者(decorator)模式能夠在不改變對象自身的基礎(chǔ)上,在程序運(yùn)行期間給對像動(dòng)態(tài)的添加職責(zé)。與繼承相比,裝飾者是一種更輕便靈活的做法。 在不改變對象自身的基礎(chǔ)上,在程序運(yùn)行期間給對象動(dòng)態(tài)地添加一些額外職責(zé) 特點(diǎn) : 可以動(dòng)態(tài)的...
摘要:訂閱模式的一個(gè)典型的應(yīng)用就是后面會(huì)寫一篇相關(guān)的讀書筆記。享元模式享元模式的核心思想是對象復(fù)用,減少對象數(shù)量,減少內(nèi)存開銷。適配器模式對目標(biāo)函數(shù)進(jìn)行數(shù)據(jù)參數(shù)轉(zhuǎn)化,使其符合目標(biāo)函數(shù)所需要的格式。 設(shè)計(jì)模式 單例模式 JS的單例模式有別于傳統(tǒng)面向?qū)ο笳Z言的單例模式,js作為一門無類的語言。使用全局變量的模式來實(shí)現(xiàn)單例模式思想。js里面的單例又分為普通單例和惰性單例,惰性單例指的是只有這個(gè)實(shí)例...
摘要:前言設(shè)計(jì)模式幾十種,閱讀了設(shè)計(jì)模式與開發(fā)實(shí)踐這本書后,個(gè)人感覺就是圍繞對象來設(shè)計(jì)的,發(fā)現(xiàn)日常寫代碼能用上的并不多,下面是常用的幾種設(shè)計(jì)模式。前端服務(wù)端可以參考我的另一個(gè)倉庫地址,一個(gè)簡單的實(shí)時(shí)聊天參考來自設(shè)計(jì)模式與開發(fā)實(shí)踐讀書筆記 前言 設(shè)計(jì)模式幾十種,閱讀了《JavaScript設(shè)計(jì)模式與開發(fā)實(shí)踐》這本書后,個(gè)人感覺js就是圍繞對象來設(shè)計(jì)的,發(fā)現(xiàn)日常寫代碼能用上的并不多,下面是常用的...
摘要:想一想,這個(gè)和我們的迭代器模式有著異曲同工的妙處,迭代器模式同樣也是遍歷選出最優(yōu)解,但是相比而言,職責(zé)鏈模式的直觀性個(gè)書寫的幸福感是遠(yuǎn)遠(yuǎn)超過迭代器模式的。 職責(zé)鏈模式其實(shí)很好理解,由于一個(gè)鏈字出賣了它的靈魂。我們可以從這個(gè)字得到很大的提示。首先這個(gè)模式一定有傳遞性,而且,節(jié)點(diǎn)是可以重復(fù)拼接的,并且每個(gè)節(jié)點(diǎn)都具有一定的過濾功能,一定的職責(zé)。 是不是想起了組合模式里的一些內(nèi)容呢? 是的,他...
閱讀 2538·2023-04-26 00:57
閱讀 911·2021-11-25 09:43
閱讀 2221·2021-11-11 16:55
閱讀 2207·2019-08-30 15:53
閱讀 3592·2019-08-30 15:52
閱讀 1459·2019-08-30 14:10
閱讀 3379·2019-08-30 13:22
閱讀 1209·2019-08-29 11:18