Chain of Responsibility 责任链模式

今天开始进入到 Behavioral design patterns,这一类的模式着重於物件之间的沟通与责任分配,就让我们接下去一起看看吧

Chain of Responsibility 责任链模式

当有一个需要处理的需求(或请求)进入系统的时候,这个系统中负责的物件会串成一个链,然後让这个需求依序被处理。在这个过程当中,每一个物件会

  • 决定如何处理这个需求
  • 决定是否将这个需求送往下一个物件

问题

假设有一间公司,准备发起一个新的 VIP 会员回馈方案,不过会员本身的点数和经验需要到一个标准值才能参与。

如果我们的 Member 类别像是下面这样

class Member {
  points: number;
  experience: number;
  constructor(points: number, experience: number){
    this.points = points
    this.experience = experience
  }
}

const memberA = new Member(1500, 2000)

接下来,我们就可以很简单写一个函式来进行判断。譬如我们希望找到点数大於 1000、经验值大於 1500 的会员来参与 VIP 会员回馈方案,实作如下:

const handler = (member: Member): boolean => {
  const { points, experience } = member
  return points > 1000 && experience > 1500
}

但如果未来我们有不同的回馈方案有不同的标准值、又或者说需要加入更多的判断条件,那麽我们就需要建立更多的 handler 函式,或者是不断的修改原有的 handler 函式。

如果我们可以用组合的方式,将不同的判断函式组合成我们需要的函式,可以随意调整,随插随用,是不是就会很方便呢?

接下来让来看看责任链模式可以怎麽处理这个问题

实作

首先,我们建立 Handler 介面,和一个抽象类别。这里我们定义 handler 当中需要有两个方法,一个是处理需求的 handle 方法,另一个是记录下一个 handler 的位置

interface Handler {
  handle(member: Member): boolean
  next(handler: Handler): Handler
}

abstract class AbstractHandler implements Handler {
  protected nextHandler: Handler

  next(handler: Handler): Handler {
    this.nextHandler = handler
    return handler
  }

  handle(member: Member): boolean {
    return false
  }
}

接着,我们就可以分别建立处理 points 和处理 experience 的 handler

class PointsHandler extends AbstractHandler {
  private limit: number;

  constructor(limit: number){
    super()
    this.limit = limit
  }

  handle(member: Member): boolean {
    if (member.points > this.limit) {
      return this.nextHandler
        ? this.nextHandler.handle(member) && true
        : true;
    }
    return false;
  }
}

class ExperienceHandler extends AbstractHandler {
  private limit: number;

  constructor(limit: number){
    super()
    this.limit = limit
  }

  handle(member: Member): boolean {
    if (member.experience > this.limit) {
      return this.nextHandler
        ? this.nextHandler.handle(member) && true
        : true;
    }
    return false;
  }
}

这里我们做了几件事情

  1. 在建立实例的时候,会设定判断的条件 (limit)
  2. handle 方法当中,我们会进行条件判断,如果不通过,就会直接回传 false;如果通过,就会看之後的 handler 是否通过,来决定最後的回传值

接下来,我们就可以分别建立 pointsHandler 和 experienceHandler

const pointsHandler = new PointsHandler(1000)
const experienceHandler = new ExperienceHandler(1500)

然後,记得把他们给串连一起

pointsHandler.next(experienceHandler)

最後,我们就可以用来判断不同 member 的状况

const memberA = new Member(1500, 2000)
const memberB = new Member(1250, 1250)
const memberC = new Member(750, 750)

pointsHandler.handle(memberA) // true
pointsHandler.handle(memberB) // false,因为 experience 不够
pointsHandler.handle(memberC) // false,因为 points 和 experience 不够

所以未来如果我们想要调整判断的条件限制(譬如降低 points 条件至 500),那麽只要透过 PointsHandlerExperienceHandler 类别建立新的实例即可。

如果我们想要调整判断的方式(譬如移除 points,新增 level),那麽我们就可以依赖 AbstractHandler 实作另外一个新的 handler,然後用 next 方法把需要的方式给组合在一起

优点与缺点

责任链模式提供了很高的扩充弹性,让我们能够依序处理需求或请求,同时不需要去修改既有的物件或函式。而缺点就是,不一定所有的请求都会被完全处理到。


<<:  Day28-保护鲸鱼人人有责(三)

>>:  Day 29 - 将 Yacht 後台储存资料提取後,送至前台渲染 Layout&deck 及 Video 版面内容区块 - 嵌入 YouTube 影片 - ASP.NET Web Forms C#

数字证书(Digital Certificate)

证书申请和回应 证书签名请求(Certificate Signing Request) 在公钥基础结...

DAY27 mongo aggregate

今天要教 mongo aggregate 中文叫做聚合 是一种将来自多个document的value...

[职场]不放过每个细节,完成一场 0 失误的专案 Demo!

每份专案都是团队尽心竭力的成果,而 Demo 就是向长官及其他部门展示团队实力的重要时刻! 但如果在...

Day 7 : 回圈-用来解决重复的事情

今天要来学习程序语言中非常重要的一个概念(功能)-回圈,回圈到底可以拿来干嘛呢?先来看个例子: 如果...

day7: CSS style 规划 - CSS in JS(emotion 使用 - 1)

在上一篇我们介绍的 CSS in JS, 那这次我们来使用 CSS in JS 的框架 emotio...