责任链模式

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

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 优雅。

缺点

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

使用场景

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

相关推荐
天下无贼!1 小时前
2024年最新版Vue3学习笔记
前端·vue.js·笔记·学习·vue
Jiaberrr1 小时前
JS实现树形结构数据中特定节点及其子节点显示属性设置的技巧(可用于树形节点过滤筛选)
前端·javascript·tree·树形·过滤筛选
赵啸林1 小时前
npm发布插件超级简单版
前端·npm·node.js
罔闻_spider2 小时前
爬虫----webpack
前端·爬虫·webpack
吱吱鼠叔2 小时前
MATLAB数据文件读写:1.格式化读写文件
前端·数据库·matlab
爱喝水的小鼠2 小时前
Vue3(一) Vite创建Vue3工程,选项式API与组合式API;setup的使用;Vue中的响应式ref,reactive
前端·javascript·vue.js
盏灯2 小时前
前端开发,场景题:讲一下如何实现 ✍电子签名、🎨你画我猜?
前端
WeiShuai2 小时前
vue-cli3使用DllPlugin优化webpack打包性能
前端·javascript
Wandra2 小时前
很全但是超级易懂的border-radius讲解,让你快速回忆和上手
前端
ice___Cpu2 小时前
Linux 基本使用和 web 程序部署 ( 8000 字 Linux 入门 )
linux·运维·前端