• 职责链模式


    什么是职责链模式?

      职责链模式的定义是解耦请求的发送者和请求的接受者,所有的对象都有机会执行,直到有对象处理请求为止。

      职责链的作用是让业务代码更具可维护性和可拓展性。

    什么时候使用职责链?

    例如:

    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

  • 相关阅读:
    让盘古分词支持最新的Lucene.Net 3.0.3
    Mac下配置GitTF来连接TFS2012
    基于Xcode4开发第一个iPhone程序:“Hello World”
    第二个iPhone应用程序:“Say Hello”
    基于MMSeg算法的中文分词类库
    为什么IDEA不推荐你使用@Autowired ?
    Spring Cloud Gateway自定义过滤器实战(观测断路器状态变化)
    Spring Cloud Gateway实战之五:内置filter
    Spring Cloud Gateway过滤器精确控制异常返回(分析篇)
    Spring Cloud Gateway实战之四:内置predicate小结
  • 原文地址:https://www.cnblogs.com/yy95/p/9932421.html
Copyright © 2020-2023  润新知