责任链模式

在讨论责任链模式之前,先来看前端经常需要实现的表单验证功能: 假如注册新用户要验证用户名,密码,手机号:

ts 复制代码
interface FormData {
  name?: string;
  password?: string;
  phone?: string;
}

const validateFormData = (formData: FormData) => {
  const { name, password, phone } = formData;
  if (name && name.length >= 3 && name.length <= 10) {
    if (password && password.length >= 6 && name.length <= 12) {
      if (phone && phone.length === 11) {
        return true;
      }
    }
  }
  return false;
};

显然,这个代码并不那么友好,当要修改验证规则时,不得不深入代码修改,不符合开闭原则。我们将代码进行优化:

ts 复制代码
const validateNamePhone = (data: Data) => {
  const { phone } = data;
  if (phone && phone.length === 11) {
    return true;
  }
  return false;
};

const validateNamePassword = (data: Data) => {
  const { password } = data;
  if (password && password.length >= 6 && password.length <= 12) {
    return validateNamePhone(data);
  }
  return false;
};

const validateName = (data: Data) => {
  const { name } = data;
  if (name && name.length >= 3 && name.length <= 10) {
    return validateNamePassword(data);
  }
  return false;
};

console.log(validateName({ name: "te" })); //false
console.log(validateName({ name: "test", password: "pa" })); //false
console.log(
  validateName({ name: "test", password: "abcdedf", phone: "13141234125" })
); //true

可以看到代码得到了优化,之前大的 if-else 被拆分成了几个小的函数,结构更加清晰,但函数里的依赖链条比较死板,如果中间增加校验步骤,就不得不打碎这个链条,深入代码进行修改。

责任链

前述的代码已经有责任链的雏形了,那么什么是责任链? 责任链模式的定义就是当请求到来时,让中间所有对象都能够有机会处理请求,从而避免了请求的发送者与后续的一些列接收者之间的耦合关系。

通常来讲,在职责链模式里,中间的对象由每一个对象对其下家的引用而连接起来形成一条链,请求在这个链上传递,链上对象都可以处理此请求。我们常举的一个例子就是,公交车上传递乘车费,乘车人只需要将钱传递给第一个人,然后由他继续传递,最后到售票员。在这个过程中乘车人并不需要知道除第一个人以外的其他的人,不需要知道给他传递钱的是男是女,售票员的高矮胖瘦,这样乘车人与后续一些列传递人的关系就解耦了。

下图是普通方式请求处理和责任链模式处理请求的一个对比图:

责任链模式类图:

实现:

ts 复制代码
abstract class Handler {
  private next: Handler;

  public getNext(): Handler {
    return this.next;
  }

  public setNext(next: Handler) {
    this.next = next;
  }
  public abstract handle(request: object): void;
}

class ConcreteHandler1 extends Handler {
  public handle(request: object) {
    console.log(`concrete handler1 excute request`);
    const next = this.getNext();
    if (next) {
      next.handle(request);
    }
  }
}

class ConcreteHandler2 extends Handler {
  public handle(request: object) {
    console.log(`concrete handler2 excute request`);
    const next = this.getNext();
    if (next) {
      next.handle(request);
    }
  }
}

class ConcreteHandler3 extends Handler {
  public handle(request: object) {
    console.log(`concrete handler3 excute request`);
    const next = this.getNext();
    if (next) {
      next.handle(request);
    }
  }
}

const main = () => {
  const handler1 = new ConcreteHandler1();
  const handler2 = new ConcreteHandler2();
  const handler3 = new ConcreteHandler3();

  handler1.setNext(handler2);
  handler2.setNext(handler3);

  handler1.handle({ message: "test" });
};

main();

对于前述表单校验的例子,用责任链模式实现如下:

ts 复制代码
abstract class Validator {
  private next: Validator;

  public getNext(): Validator {
    return this.next;
  }

  public setNext(next: Validator) {
    this.next = next;
  }
  public abstract validate(request: Data): void;
}

class NameValidator extends Validator {
  public validate(request: Data) {
    const { name } = request;
    const isValid = name && name.length >= 3 && name.length <= 10;
    if (isValid) {
      const next = this.getNext();
      if (next) {
        return next.validate(request);
      }
      return true;
    }
    return false;
  }
}

class PasswordValidator extends Validator {
  public validate(request: Data) {
    const { password } = request;
    const isValid = password && password.length >= 6 && password.length <= 12;
    if (isValid) {
      const next = this.getNext();
      if (next) {
        return next.validate(request);
      }
      return true;
    }
    return false;
  }
}

class PhoneValidator extends Validator {
  public validate(request: Data) {
    const { phone } = request;
    const isValid = phone && phone.length === 11;
    if (isValid) {
      const next = this.getNext();
      if (next) {
        return next.validate(request);
      }
      return true;
    }
    return false;
  }
}

//客户端程序
const main = () => {
  const nameValidator = new NameValidator();
  const passwordValidator = new PasswordValidator();
  const phoneValidator = new PhoneValidator();

  nameValidator.setNext(passwordValidator);
  passwordValidator.setNext(phoneValidator);

  console.log(nameValidator.validate({ name: "test" })); //false
  console.log(nameValidator.validate({ name: "test", password: "pa" })); //false
  console.log(
    nameValidator.validate({
      name: "test",
      password: "abcdedf",
      phone: "13141234125",
    })
  ); //true
};

main();

可以看到通过责任链实现校验后,更加灵活了。假如要添加身份证校验的功能,只需要实现 IdValidator,不需要侵入原来的代码,在客户端只需要重新添加处理节点就可以了。

在上述的例子中,责任链当前节点处理完成之后,自己将请求传递给下一个处理节点处理,事实上请求的传递也可以统一由请求管理对象统一传递,只要符合责任链的核心概念,都可以叫责任链模式。

实现大致如下:

ts 复制代码
abstract class Validator {
  private next: Validator;

  public getNext(): Validator {
    return this.next;
  }

  public setNext(next: Validator) {
    this.next = next;
  }
  public abstract validate(request: Data): boolean;
}

class NameValidator extends Validator {
  public validate(request: Data) {
    const { name } = request;
    const isValid = name && name.length >= 3 && name.length <= 10;
    return isValid;
  }
}

class PasswordValidator extends Validator {
  public validate(request: Data) {
    const { password } = request;
    const isValid = password && password.length >= 6 && password.length <= 12;
    return isValid;
  }
}

class PhoneValidator extends Validator {
  public validate(request: Data) {
    const { phone } = request;
    const isValid = phone && phone.length === 11;
    return isValid;
  }
}

class Chain {
  nodes: Validator[] = [];
  constructor(nodes: Validator[]) {
    this.nodes = nodes;
  }

  validate(request: Data) {
    for (let i = 0; i < this.nodes.length; i += 1) {
      const node = this.nodes[i];
      const isValid = node.validate(request);
      if (!isValid) {
        return false;
      }
    }

    return true;
  }
}

const main = () => {
  const nameValidator = new NameValidator();
  const passwordValidator = new PasswordValidator();
  const phoneValidator = new PhoneValidator();

  const chain = new Chain([nameValidator, passwordValidator, phoneValidator]);

  console.log(chain.validate({ name: "test" }));
  console.log(chain.validate({ name: "test", password: "pa" }));
  console.log(
    chain.validate({ name: "test", password: "abcdedf", phone: "13141234125" })
  );
};

总结

优点

  • 解耦

    请求的发送者最多只需要知道第一个处理者,而不用关心请求后续被谁以及如何处理。

  • 灵活

    当新增处理者时,只需要实现处理逻辑,然后插入到对应的位置就可以了,客户端不会感知到处理器的变化。

  • 符合开闭原则

    比 if-else 优雅。

缺点

  • 责任链太长,调试可能比较麻烦。
  • 责任链太长或者处理者处理速度慢,可能会影响系统的性能。

使用场景

有多个对象可以处理同一个请求,具体哪个对象处理该请求由运行时刻自动确定。比如,事件处理、请求拦截、中间件等。

相关推荐
excel8 小时前
为什么在 Three.js 中平面能产生“起伏效果”?
前端
excel9 小时前
Node.js 断言与测试框架示例对比
前端
天蓝色的鱼鱼11 小时前
前端开发者的组件设计之痛:为什么我的组件总是难以维护?
前端·react.js
codingandsleeping11 小时前
使用orval自动拉取swagger文档并生成ts接口
前端·javascript
石金龙12 小时前
[译] Composition in CSS
前端·css
白水清风12 小时前
微前端学习记录(qiankun、wujie、micro-app)
前端·javascript·前端工程化
Ticnix12 小时前
函数封装实现Echarts多表渲染/叠加渲染
前端·echarts
用户221520442780012 小时前
new、原型和原型链浅析
前端·javascript
阿星做前端12 小时前
coze源码解读: space develop 页面
前端·javascript
叫我小窝吧12 小时前
Promise 的使用
前端·javascript