设计模式之职责链模式vs策略模式

5,562 阅读4分钟
学习背景

  1. 针对if/if…else语句,或者switch...case 的众多使用的重构
  2. 函数、组件设计的思考

什么是职责链模式(责任链模式)?

为了避免请求发送者与多个请求处理者耦合在一起,将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。(简言:一个请求有多个对象可以处理,但每个对象的处理条件或权限不同。)
主要构成者:
  1. 抽象处理者(handler):后继处理连接的接口
  2. 具体处理者(concrete handler):抽象处理者的具体实现方式,处理本次请求或者向后传递
  3. 客户类(client):创建处理链,并向具体处理者对象提交请求,不关心内部发生了什么

案例代码1,公司促销活动,返优惠券:

const order = (orderType,pay,stock) {  if(orderType === 1){    if(pay === true){      console.log('500元定金,返回100优惠券')    } else {      if(stock > 0 ){        consle.log('普通购买,无优惠券')      } else {        console.log('库存不足')      }    }  } else if(orderType === 2){    if(pay === true){      console.log('200元定金,返回50优惠券')    } else {      if(stock > 0 ){        consle.log('普通购买,无优惠券')      } else {        console.log('库存不足')      }    }              } else {    if(stock > 0 ){        consle.log('普通购买,无优惠券')      } else {        console.log('库存不足')      }   }}

职责链设计思想,重构思路
  • 将不同的购买模式单独封装成函数
  • 实现链式函数,支持追加函数;在不符合时,实现递交

// 模式封装,具体处理者const order500 = function(orderType,pay,stock){    if(orderType === 1 && pay === true){        console.log('500元定金,返回100优惠券')    } else {        return 'nextSuccessor';    }}const order200 = function(orderType,pay,stock){    if(orderType === 2 && pay === true){        console.log('200元定金,返回50优惠券')    } else {        return 'nextSuccessor';    }}const orderNormal = function(orderType,pay,stock){    if(stock > 0 ){        console.log('普通购买,无优惠券')      } else {        console.log('库存不足')      }}// 原型链,链式传递,抽象处理者const 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); if(ret === '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,false,0);chainOrder500.passRequest(1,true,10);chainOrder500.passRequest(2,true,11);chainOrder500.passRequest(2,false,1);

职责链模式关系图


职责链模式总结

  • 优点:
    • 各司其职,符合类的最小封装原则
    • 工作流程可以自由组合
    • 类与类之间的低耦合
  • 缺点:
    • 具体处理者类太多,会影响执行效率
  • 注意:职责链建立的合理性


什么是策略模式?

该模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户。策略模式属于对象行为模式,它通过对算法进行封装,把使用算法的责任和算法的实现分割开来,并委派给不同的对象对这些算法进行管理。(简言:把一系列的算法封装起来,让其可以相互替换,封装的算法具有一定的独立性,不随客户端的变化而变化。)

主要构成者:

  1. 抽象策略类(Strategy):是一个公共接口,由不同的算法实现,环境类使用这个接口调用不同的算法
  2. 具体策略类(Concrete Strategy):具体算法的实现
  3. 环境类(Context):客户端调用的一个策略类的引用

案例代码2,产品促销,按用户等级返现:

return30(price) {    // 返现30 do something}retuen50(price) {    // do something}switch(user.cheaperLevel) {    case 1: return30()    break    case 2: return50()    break    ...}// 函数全局暴露,不能根据不同情况去使用想要的方法

策略模式设计重构

const PriceStrategy = function(){// 内部算法对象, 具体策略类    const stragtegy = {        return30: function(price){        },        return60: function(price){        },    }// 策略算法调用接口,抽象策略类    return function(cheaperLevel,price) {        return stragtegy[cheaperLevel] && stragtegy[cheaperLevel](price)    }}// 使用方式,环境类let price = PriceStrategy('return30','321.56')console.log(price)

案例代码3,表单验证,策略模式重构:

const inputStrategy = () => {    const strategy = {        notNull : (val) => {            return /\s+/.test(val) ? '请输入' : '';        },        number :(val) => {            return val.isNaN ? '请输入数字' :'';        }    }    return {        check:(type,value)=>{           return  strategy[type](value) ;        },        // 肯定策略不够用,为它添加一个自定义的策略Api        addStrategy:(type,fn)=>{            strategy[type] = fn ;        }    }}// 算法调用let inputValue = document.getElmentById('target')let isVaild = inputStrategy.check('number',inputValue)

策略模式总结

  • 优点
    • 避免多重条件语句
    • 可复用
    • 开闭原则的完美支持,灵活增加新算法
    • 算法的使用和实现的分离
  • 缺点
    • 客户端使用必须理解所有算法的区别(备注/注释)
    • 有很多的策略类


两者之间的区别

职责链模式
  • 解决更多的是 条件属于模糊的判断,执行不同的逻辑,比如if(true || false) / else if() /…
  • 将判断条件内置于函数中,条件不符合需要链式传递继续执行函数
策略模式
  • 有确定的判断条件,比如switch...case
  • 直接根据确定的判断条件调用