什么是职责链模式?
职责链模式的定义是解耦请求的发送者和请求的接受者,所有的对象都有机会执行,直到有对象处理请求为止。
职责链的作用是让业务代码更具可维护性和可拓展性。
什么时候使用职责链?
例如:
1、给一个未知的元素里面添加一个按钮,并且给这个按钮添加一个点击事件
2、电商平台的优惠券:交满500元定金,得100优惠券;交满200元定金,50优惠券;没交定金的,按普通用户算,没有优惠券。
等等...
需求多变的时候,需要考虑适应多种情况,可以考虑职责链是否可以封装业务代码。
怎么用职责链模式实现上面第二个需求?
考虑到未来可能会有新的类型,比如交满300元定金,得60元优惠券,然后不要交满200元定金这个需求,如何编写代码来更好的适配我们多变的需求?
let order500 = function( orderType, pay, stock ){ if ( orderType === 1 && pay === true ){ console.log( '500 元定金预购,得到100 优惠券' ); }else{ return 'nextSuccessor'; // 我不知道下一个节点是谁,反正把请求往后面传递,解耦 } }; let order200 = function( orderType, pay, stock ){ if ( orderType === 2 && pay === true ){ console.log( '200 元定金预购,得到50 优惠券' ); }else{ return 'nextSuccessor'; // 我不知道下一个节点是谁,反正把请求往后面传递 } }; let orderNormal = function( orderType, pay, stock ){ if ( stock > 0 ){ console.log( '普通购买,无优惠券' ); }else{ console.log( '手机库存不足' ); } }; let Chain = function( fn ){ this.fn = fn; this.successor = null; }; Chain.prototype.setNextSuccessor = function( successor ){ return this.successor = successor; }; Chain.prototype.passRequest = function(){ let ret = this.fn.apply( this, arguments );//从执行chainOrder500开始,如果满足条件就会停止往下执行 if ( ret === 'nextSuccessor' ){//如果执行chainOrder500没有满足条件就会返回nextSuccessor,执行下个函数 return this.successor && this.successor.passRequest.apply( this.successor, arguments ); } return ret; }; //创建对象-节点 let chainOrder500 = new Chain( order500 ); let chainOrder200 = new Chain( order200 ); let chainOrderNormal = new Chain( orderNormal ); //将对象(节点)串成链 chainOrder500.setNextSuccessor( chainOrder200 ); chainOrder200.setNextSuccessor( chainOrderNormal ); //执行测试 chainOrder500.passRequest( 1, true, 500 ); // 输出:500 元定金预购,得到100 优惠券 chainOrder500.passRequest( 2, true, 500 ); // 输出:200 元定金预购,得到50 优惠券 chainOrder500.passRequest( 3, true, 500 ); // 输出:普通购买,无优惠券 chainOrder500.passRequest( 1, false, 0 ); // 输出:手机库存不足
项目中的实际场景:
多种类型的复制链接的操作,也可以将业务代码用职责链来封装
1、
copyLink() { const result = copyMethods({ content: this.qr }) if(result){ this.$message.success('复制图片url成功!') } else { this.$message.error('复制失败') } },
2、
copy(content, e){ const el = e.target.parentNode.querySelector('.piece-content') const result = copyMethods({ content, el }) if(result){ this.$message.success('成功复制文本') } else { this.$message.error('复制失败') } },
具体实现:
function copyWithRange({ el }){ if(!el || !document.createRange) return 'next' let range = document.createRange() range.selectNode(el) window.getSelection().addRange(range) const result = document.execCommand('copy') window.getSelection().removeAllRanges() return result } function copyWithClipEvent({ content }){ function copy (e) { // content = `<${content}/>` e.clipboardData.setData('text/plain', content) e.preventDefault() } // 添加 copy 内容 document.addEventListener('copy',copy) // 执行 copy 命令 const result = document.execCommand('copy') // 移除绑定事件 document.removeEventListener('copy',copy) return result } function copyWithInput({ content }){ let aux = document.createElement('input') aux.setAttribute('value', content) document.body.appendChild(aux) aux.select() window.getSelection().toString() const result = document.execCommand('copy') document.body.removeChild(aux) return result } //职责链封装三个快捷复制的操作 function copyMethods({ content, el }){ const fns = [ copyWithRange, copyWithClipEvent, copyWithInput, ] const fnChains = chain(fns) return fnChains({ content, el }) } function after(fn, afterFn){ return function(...args){ const result = fn.apply(this, args) if(result === 'next'){ return afterFn.apply(this, args) } return result } } function chain(fns){ return fns.reduce((pre, next) => { return after(pre, next) }) }
参考资料:
https://zhuanlan.zhihu.com/p/28327127
https://www.cnblogs.com/xiaohuochai/p/8040195.html