设计模式:(Chain of Responsibility Pattern)责任链模式

210 阅读3分钟

欢迎来到 TypeScript 中的设计模式系列,它介绍了使用TypeScript进行Web开发的一些有用的设计模式。

系列文章如下:

设计模式对 Web 开发人员来说非常重要,我们可以通过掌握它们来编写更好的代码。在本文中,我将使用TypeScript来介绍责任链模式

责任链模式是一种通过为多个对象提供处理请求的机会来避免请求的发送者和接收者之间耦合的方法。在责任链模式中,许多对象通过从每个对象到下一个对象的引用连接起来,形成一个链。请求沿着链传递,直到链中的一个对象决定处理请求。

举个例子:

公司不同岗位职责不同,权限也不同。以公司的请假流程为例,我请假的时候,只需要组长批准,不需要转给主管和总监。如果责任链中的某个环节无法处理当前的请求,如果有下一个环节,则将请求转发到下一个环节进行处理。

在软件开发过程中,对于责任链来说,一个常见的应用场景就是中间件。下面我们来看看如何使用责任链来处理请求。

下面看看UML图:

iShot_2022-10-08_20.06.53.png

上图中,我们定义一个Handler接口,该接口中有两个方法:

  • use(h:Handler): Handler 用来注册handler(中间件)
  • get(url:string, callback: (data:any) => void): void 注册一个获取请求处理程序
interface Handler {
    use(h:Handler):Handler;
    get(url:string, callback: (data:any) => void): void 
}

紧接着我们定义一个抽象类AbstractHandler,封装责任链的处理逻辑。即组合不同的处理程序,形成一个引用链。

abstract AbstractHandler implement Handler {
    next!:Handler
    use(h:Handler) {
        this.next = h
         // 通过next,形成绑定链
         // 例如:route.use().use()
        return this.next
    }
    
    get(url: string, callback: (data: any) => void) {
        if (this.next) {
            return this.next.get(url, callback);
        }
        return null
     }
}

基于AbstractHandler抽象类,我们分别定义AuthMiddlewareLoggerMiddleware中间件子类。AuthMiddleware 中间件用于处理用户认证, LoggerMiddleware中间件用于输出请求日志。

class AuthMiddleware extends AbstractHandler {
  isAuthenticated: boolean;
  constructor(username: string, password: string) {
    super();
    
    this.isAuthenticated = false;
    if (username === "kevin" && password === "password123") {
      this.isAuthenticated = true;
    }
  }  get(url: string, callback: (data: any) => void) {
    if (this.isAuthenticated) {
      return super.get(url, callback);
    } else {
      throw new Error("Not Authorized");
    }
  }
}

让我们来定义一个Route类并注册AuthMiddlewareLoggerMiddleware中间件

class Route extends AbstractHandler {
  urlDataMap: { [key: string]: any };
  constructor() {
    super();
    this.urlDataMap = {
      '/api/todos': [{ title: 'Learn Design Pattern' }],
      '/api/random': () => Math.random(),
    };
  }
  get(url: string, callback: (data: any) => void) {
    super.get(url, callback);
    if (this.urlDataMap.hasOwnProperty(url)) {
      const value = this.urlDataMap[url];
      const result = typeof value === 'function' ? value() : value;
      callback(result);
    }
  }
}

定义好Route类后,我们可以使用它来注册中间件

const route = new Route();

route
  .use(new AuthMiddleware('kevin', 'password123'))
  .use(new LoggerMiddleware());

route.get('/api/todos', (data) => {
  console.log(JSON.stringify({ data }, null, 2));
});
route.get('/api/random', (data) => {
  console.log(data);
});
  • 第一个use将route实例的next指向AuthMiddleware实例,第二个use将AuthMiddleware实例的next指LoggerMiddleware实例上,以此类推,类似于指针。 image.png
  • 当调用route.get()方法时,将调用this.next.get沿着链一层一层往下走,最后得出结果如下:

image.png

最后总结一下责任链模式的使用场景:

  • 想要向多个对象之一提交请求而不明确指定收件人。
  • 有多个对象可以处理一个请求,哪个对象处理请求是在运行时自动确定的,客户端只需要将请求提交到链上。