ChenPt / dailyNote

dailyNode for myself
https://github.com/ChenPt/dailyNote/issues
0 stars 0 forks source link

2020/06/08 责任链模式 #42

Open ChenPt opened 4 years ago

ChenPt commented 4 years ago

日常编写控制逻辑的代码,使用得最多的应该是if elseswitch case了,对于简单的判断条件,且判断条件的可能取值较少,我们可以直接使用if else编写。举个例子

  1. 简单的判断
    if (value < 0) return '取值必须大于0'
  2. 简单的判断 - 可能取值较多
    let result = ''
    switch(value) {
    case 'A':
    result = 'Acer'
    break
    case 'B':
    result = 'BiliBili'
    break
    case 'C':
    result = 'Carry'
    break
    }
    console.log(result)

    这两种情况使用if else或者switch case或者表驱动法(Map结构)都比较合适,如果是复杂一点的条件的话,就不太适合。举个例子

    let result = ''
    if  (type === 'edit' && user === 'A') {
    result = 'editA'
    } else if (type === 'edit' && user === 'B') {
    if (age > 20) {
    result = 'editB20'
    return
    }
    result = ' editBLess20'
    }

    如果后续还需要扩展,需要判断type为new类型的,增加其他条件。或者是判断条件有所改动,这段代码的逻辑就会变得比较不直观,扩展也需要改动到原来的逻辑处理。

    let result = ''
    if  ((type === 'edit' || type === 'new') && user === 'A') {
    result = 'editA'
    } else if (type === 'edit' && user === 'B') {
    if (age > 20) {
    result = 'editB20'
    return
    }
    result = ' editBLess20'
    }
    return result

    分析下这段代码,这段代码简单概括就是根据条件取得对应的result,意思就是只要有其中某一个条件符合,则会取得result对应的值并返回。 类似的还有这种代码

    let msg = '';
    if (!itemCode) {
    msg = '物料编号不能为空'
    return msg
    }
    if (!qty) {
    msg = '数量不能为空'
    return msg
    }
    if (!price) {
    msg = '价格不能为空'
    return msg
    }
    if (!status) {
    msg = '状态不能为空'
    return msg
    }

    这个例子简单概括也是根据多个字段的值,取得msg,返回。如果是简单地判断某一个字段的值,则可以使用switch case去写,也可以通过Map的形式去写

    const map = {
    A: 5,
    B: 4,
    C: 3,
    D: 2,
    E: 1
    }
    const getValue = value => map[value] || ''

    但如果是判断多个字段,或者多个条件耦合的判断,场景比较复杂的,通过if else去实现代码会比较丑且不易读,扩展性也差

    责任链模式

    责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为型模式。 在这种模式中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。 -- 菜鸟教程

通常,我们理解中的责任链模式有两种。

  1. 第一种是请求可以被某一个handler处理,处理完之后,将请求继续抛给下一个handler处理。
  2. 请求被某一个handler处理,处理完之后请求并不会向下家传递。

责任链模式,其实我们一直有接触。如DOM的事件冒泡与事件捕获就是一个责任链的设计思想,属于上述的第一种类型。

事件冒泡:一个事件发生,从底层节点往上开始冒泡,当有节点绑定了该事件时,则会触发绑定的回调函数,且事件还会继续往上冒泡,直至最顶层节点。不过手动阻止冒泡事件需要自己编写,event.stopPropagation()

使用责任链模式

设计的接口如下 每一个链条节点都设置了三个字段,result,condition,actionresult为匹配到后返回的结果,condition为匹配的条件,条件为true时,则当前链条节点匹配成功,处理action,并返回结果,停止继续传播。

责任链类。setNext方法可供各个节点串联起来。getResult方法返回匹配到的节点的result参数。

生成规则链的函数,利用reduce方法快速构造

具体应用

数据校验。SN不能为空,且不能为中文,且不能包含感叹号(全/半角),且位数不能小于5 当然,这里完全可以不用构造责任链,由于使用的是数组结构,可以利用这个顺序数据结构的特性,只需根据责任链的思想去编写即可

// 直接用数组方法去编写
export function checkSn(value): {res: boolean, message: string} {
  const dutys = [
    {
      condition: /[\u4e00-\u9fa5]+/g.test(value),
      result: '不能包含中文'
    },
    {
      condition: /\s+/g.test(value),
      result: '不能包含空格'
    },
    {
      condition: /[\!|\!]+/g.test(value),
      result: '不能包含感叹号'
    },
    {
      condition: /^\S{0,4}$/g.test(value),
      result: '长度不能小于5'
    },
    {
      condition: true,
      result: ''
    }
  ];
  const result = dutys.find(duty => duty.condition).result || '';
  // const result = createDutyChain(dutys).getResult();
  return {
    res: !result,
    message: result
  };
}

责任链的优缺点

优点:

  1. 可动态增加或修改handler
  2. 扩展性高
  3. 只需将值传入链条中,然后取得结果,无需关注内部节点的处理逻辑

缺点:

  1. 只能从头到尾按顺序传递
  2. 不能保证请求一定会被处理