前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >JavaScript设计模式之职责链模式

JavaScript设计模式之职责链模式

作者头像
一粒小麦
发布2019-11-18 22:46:28
3310
发布2019-11-18 22:46:28
举报
文章被收录于专栏:一Li小麦

职责链模式

先看一个典型的场景:早上上班坐公交,挤不上前门,只能从后门上。这时候就会拜托别人递公交卡。公交卡一个个向前传递——最后遇到了能够得着刷卡机的那个人。

再看一个OA场景:我要写信给公司老板,信的传递通常是:

代码语言:javascript
复制
项目组管理->部门->事业部->总部->公司大佬

职责链模式的定义是:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系,将这些对象连成一条链表结构,并沿着这条链传递该请求,直到有一个对象处理它为止。

写信这种事,如果没有合适的表达渠道,可能给所有人都写信,对方不能处理又把信退回给你,然后你投给下个人,用图表述是这样的:

职责链模式的名字非常形象,一系列可能会处理请求的对象被连接成一条链,请求在这些对象之间依次传递,直到遇到一个可以处理它的对象,我们把这些对象称为链中的节点,如图:

职责链模式与链表数据结构有千丝万缕的关系。笔者写过一篇文章: 《javascript数据结构和算法》读书笔记(3):链表 阅读它或许会更好地帮助理解本文。

职责链模式的优势在于,弱化了请求和服务对象之间的联系。一个大型应用中,只需要指导谁是职责链表的第一个接受者,即可与之建立通信。

条件渲染问题

这时实际工作中的一个问题:一个数学试题详情,实现了我们所能想得到的一些功能。因此可能会有多处调用场景: >> 是老师还是学生?

  • 学生做题界面:允许做题,提交,不允许看答案
  • 如果是老师(teacher),需要判断teacherType
  • 如果是数学老师,需要判断subtype
    • 创作者(tauthor)视图:试卷可以看到试题单题分析,分数统计
    • 读者(reader)视图:可以看到试题,不允许做题,不允许看统计
    • 审核者(approver)视图:可以做题,可以看答案。
  • 非数学老师:渲染读者视图

试用代码设计这个组件。

代码语言:javascript
复制
const order=(user)=>{  if(user.userType=='student'){    console.log('渲染学生视图')  }else if(user.userType=='teacher'){    if(user.subject=='math'){      if(user.subType=='author'){        console.log('渲染创作视图')      }else if(user.subType=='reader'){        console.log('渲染读者视图')      }else if(userType=='approver'){        console.log('渲染审核者视图')      }    }else{      console.log('渲染读者视图')    }  }}

这绝对算不上一个好看的代码。对后期维护造成了巨大的开销。

设想判断信息的传递过程,可以把每个判断条件作为一个独立的小函数,当遇到不能处理的请求,则在返回值中传递一个next的信息。那么我们可以对三个判断条件各写一个方法:

代码语言:javascript
复制
const userType=(user)=>{  if(user.userType=='student'){    console.log('渲染学生视图')  }else{    // 处理不了就next    return 'next';  }}
const subject=(user)=>{
  if(user.subject!=='math'){    console.log('渲染读者视图');  }else{    return 'next';  }}
const subType=(user)=>{  if(user.subType=='author'){    console.log('渲染创作视图')  }else if(user.subType=='reader'){    console.log('渲染读者视图')  }else if(user.subType=='approver'){    console.log('渲染审核者视图')  }}

接下来就是包装这三个小方法的函数,:

代码语言:javascript
复制
class Chain{  constructor(fn){    this.fn=fn;    this.next=null;  }    // 负责设置指针  setNext(next){    return this.next=next;  }    // 处理,传递请求  passReq(){    const ret=this.fn.apply(this,arguments);    if(ret=='next'){      return this.next&&this.next.passReq.apply(this.next,arguments)    }    return ret;  }}

最后调用:

代码语言:javascript
复制
// 生成链let _userType=new Chain(userType);let _subject=new Chain(subject);let _subType=new Chain(subType);
// 设置顺序链条_userType.setNext(_subject);_subject.setNext(_subType);
_userType.passReq({  userType:'teacher',  subject:'math',  subType:'approver'})

在这种模式下,如果要做需求修改,就不必去动那些庞大的ifelse语句,只需要往链表中添加一个新的结点,改变其指针就行了。

职责链模式不只是负责优化条件语句,完全可以用来描述一个事务的处理过程,比如说,处理不了就甩锅——用trycatch包起来就可以了。

代码语言:javascript
复制
const fn=new Chain(()=>{  try{    // todo..  }catch(e){    return 'next'  }})

异步处理

命令模式和组合模式中,我们用了promise、async/await来处理异步请求。如果不用ES6+的语句。有什么办法处理异步的情况呢?

这时候返回next已经没用了。可以在原型链上设置一个next方法。

代码语言:javascript
复制
class Chain{  // ...
  next(){    return this.next&&this.next.passReq.apply(this.next,arguments);  }}

异步调用这个next方法

代码语言:javascript
复制
const fn1=new Chain(function(){  console.log('step1');});
const fn2=new Chain(function(){  console.log('step2');  setTimeout(()=>{    this.next();  })});
const fn3=new Chain(function(){  console.log('step3')})
fn1.setNext(fn2).setNext(fn3);fn.passReq()

优缺点

如前所述,职责链优势在于解耦复杂的联系。只需要向链头发送请求,就可以得到合适走过整个处理流程(但不一定得到得到期望的结果,所以需要在链尾做容错处理)。其实,在试卷例子中,你只要知道用户不是学生,就可以向链表的第二个结点发送请求。这在ifelse中是做不到的。

当然,链条过长,也会带来一些性能损耗。

小结

在JavaScript开发中,职责链模式是最容易被忽视的模式之一。实际上只要运用得当,职责链模式可以很好地帮助我们管理代码,降低发起请求的对象和处理请求的对象之间的耦合性。

职责链中的节点数量和顺序是可以自由变化的,我们可以在运行时决定链中包含哪些节点。无论是作用域链、原型链,还是DOM节点中的事件冒泡,我们都能从中找到职责链模式的影子。职责链模式还可以和组合模式结合在一起,用来连接部件和父部件,或是提高组合对象的效率。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-11-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 一Li小麦 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 条件渲染问题
  • 异步处理
  • 优缺点
  • 小结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档