什麼是職責鏈模式? 重要性:4 星,在項目中能對 if-else 語句進行優化 定義:避免請求發送者與接收者耦合在一起,讓多個對象都有可能接收請求,將這些對象連接成一條鏈,並且沿著這條鏈傳遞請求,直到有對象處理它為止。 主要解決:職責鏈上的處理者負責處理請求,客戶只需要將請求發送到職責鏈上即可,無須 ...
什麼是職責鏈模式?
重要性:4 星,在項目中能對 if-else 語句進行優化
定義:避免請求發送者與接收者耦合在一起,讓多個對象都有可能接收請求,將這些對象連接成一條鏈,並且沿著這條鏈傳遞請求,直到有對象處理它為止。
主要解決:職責鏈上的處理者負責處理請求,客戶只需要將請求發送到職責鏈上即可,無須關心請求的處理細節和請求的傳遞,所以職責鏈將請求的發送者和請求的處理者解耦了。
何時使用:在處理消息的時候以過濾很多道。
如何解決:攔截的類都實現統一介面。
應用實例: 1、紅樓夢中的"擊鼓傳花"。 2、JS 中的事件冒泡。 3、JAVA WEB 中 Apache Tomcat 對 Encoding 的處理,Struts2 的攔截器,jsp servlet 的 Filter。
優點: 1、降低耦合度。它將請求的發送者和接收者解耦。 2、簡化了對象。使得對象不需要知道鏈的結構。 3、增強給對象指派職責的靈活性。通過改變鏈內的成員或者調動它們的次序,允許動態地新增或者刪除責任。 4、增加新的請求處理類很方便。
缺點: 1、不能保證請求一定被接收。 2、系統性能將受到一定影響,而且在進行代碼調試時不太方便,可能會造成迴圈調用。 3、可能不容易觀察運行時的特征,有礙於除錯。
使用場景: 1、有多個對象可以處理同一個請求,具體哪個對象處理該請求由運行時刻自動確定。 2、在不明確指定接收者的情況下,向多個對象中的一個提交一個請求。 3、可動態指定一組對象處理請求。
場景Demo
場景:某電商針對已付過定金的用戶有優惠政策,在正式購買後,已經支付過 500 元定金的用戶會收到 100 元的優惠券,200 元定金的用戶可以收到 50 元優惠券,沒有支付過定金的用戶只能正常購買。
// orderType: 表示訂單類型,1:500 元定金用戶;2:200 元定金用戶;3:普通購買用戶 // pay:表示用戶是否已經支付定金,true: 已支付;false:未支付 // stock: 表示當前用於普通購買的手機庫存數量,已支付過定金的用戶不受此限制 const order = function( orderType, pay, stock ) { if ( orderType === 1 ) { if ( pay === true ) { console.log('500 元定金預購,得到 100 元優惠券') } else { if (stock > 0) { console.log('普通購買,無優惠券') } else { console.log('庫存不夠,無法購買') } } } else if ( orderType === 2 ) { if ( pay === true ) { console.log('200 元定金預購,得到 50 元優惠券') } else { if (stock > 0) { console.log('普通購買,無優惠券') } else { console.log('庫存不夠,無法購買') } } } else if ( orderType === 3 ) { if (stock > 0) { console.log('普通購買,無優惠券') } else { console.log('庫存不夠,無法購買') } } } order( 3, true, 500 ) // 普通購買,無優惠券
下麵用職責鏈模式改進代碼
const order500 = function(orderType, pay, stock) { if ( orderType === 1 && pay === true ) { console.log('500 元定金預購,得到 100 元優惠券') } else { order200(orderType, pay, stock) } } const order200 = function(orderType, pay, stock) { if ( orderType === 2 && pay === true ) { console.log('200 元定金預購,得到 50 元優惠券') } else { orderCommon(orderType, pay, stock) } } const orderCommon = function(orderType, pay, stock) { if (orderType === 3 && stock > 0) { console.log('普通購買,無優惠券') } else { console.log('庫存不夠,無法購買') } } order500( 3, true, 500 ) // 普通購買,無優惠券
改造後可以發現代碼相對清晰了,但是鏈路代碼和業務代碼依然耦合在一起,進一步優化:
// 業務代碼 const order500 = function(orderType, pay, stock) { if ( orderType === 1 && pay === true ) { console.log('500 元定金預購,得到 100 元優惠券') } else { return 'nextSuccess' } } const order200 = function(orderType, pay, stock) { if ( orderType === 2 && pay === true ) { console.log('200 元定金預購,得到 50 元優惠券') } else { return 'nextSuccess' } } const orderCommon = function(orderType, pay, stock) { if (orderType === 3 && stock > 0) { console.log('普通購買,無優惠券') } else { console.log('庫存不夠,無法購買') } } // 鏈路代碼 const chain = function(fn) { this.fn = fn this.sucessor = null } chain.prototype.setNext = function(sucessor) { this.sucessor = sucessor } chain.prototype.init = function() { const result = this.fn.apply(this, arguments) if (result === 'nextSuccess') { this.sucessor.init.apply(this.sucessor, arguments) } } const order500New = new chain(order500) const order200New = new chain(order200) const orderCommonNew = new chain(orderCommon) order500New.setNext(order200New) order200New.setNext(orderCommonNew) order500New.init( 3, true, 500 ) // 普通購買,無優惠券
重構後,鏈路代碼和業務代碼徹底地分離。假如未來需要新增 order300,那隻需新增與其相關的函數而不必改動原有業務代碼。
另外結合 AOP 還能簡化上述鏈路代碼:
// 業務代碼 const order500 = function(orderType, pay, stock) { if ( orderType === 1 && pay === true ) { console.log('500 元定金預購,得到 100 元優惠券') } else { return 'nextSuccess' } } const order200 = function(orderType, pay, stock) { if ( orderType === 2 && pay === true ) { console.log('200 元定金預購,得到 50 元優惠券') } else { return 'nextSuccess' } } const orderCommon = function(orderType, pay, stock) { if (orderType === 3 && stock > 0) { console.log('普通購買,無優惠券') } else { console.log('庫存不夠,無法購買') } } // 鏈路代碼 Function.prototype.after = function(fn) { const self = this return function() { const result = self.apply(self, arguments) if (result === 'nextSuccess') { return fn.apply(self, arguments) // 這裡 return 別忘記了~ } } } const order = order500.after(order200).after(orderCommon) order( 3, true, 500 ) // 普通購買,無優惠券
職責鏈模式比較重要,項目中能用到它的地方會有很多,用上它能解耦 1 個請求對象和 n 個目標對象的關係。