【设计模式】使用策略模式优化表单校验逻辑

发布于:2024-04-29 ⋅ 阅读:(28) ⋅ 点赞:(0)

什么是策略?

所谓策略,就是根据已知条件决定要做出怎样的行为。

举个栗子:我要实现一个表单校验功能,要求 name 不能为空且长度必须大于 2 且小于 4,age 不能为空且必须为纯数字。

这样的判断逻辑直接用 if-else 就可以实现:

function valid() {
  // 表单校验逻辑
  if (!form.name) {
    alert("name 不能为空");
    return false;
  }

  if (form.name.length < 2) {
    alert("name 长度必须大于 2 ");
    return false;
  }

  if (form.name.length > 4) {
    alert("name 长度必须小于 4");
    return false;
  }

  if (!form.age) {
    alert("age 不能为空");
    return false;
  }

  if (!/^\d+$/.test(form.age)) {
    alert("age 只能为数字");
    return false;
  }

  return true;
}

这种实现方式有几个缺点:

  • 最终实现的 valid() 函数比较庞大,并且包含了很多 if-else 语句,需要在这里覆盖所有的校验规则。
  • 算法复用性差,如果其他地方也要进行类似的校验,就需要将这些校验逻辑到处复制粘贴。

策略模式就是为了解决这些问题而生的。

什么是策略模式?

策略模式的核心思想是将算法与使用对象分开,对象不是直接实现单个算法,而是在执行时决定要使用哪些算法。

它基于组合优于继承的原则,通过对一系列算法的封装,使它们在运行时可以互换。

这使得对象更加灵活和可重用,因为可以轻松添加或修改不同的策略,而无需更改对象的核心代码。

以上面的表单校验逻辑为例,这段代码中有四个策略: 字段不能为空字段长度必须大于x字段长度必须小于x字段只能为数字

用策略模式优化表单校验逻辑

优化思路:

  1. 隔离校验逻辑。
  2. 封装校验类,统一管理校验逻辑。

Validator 类中封装所有校验策略,并提供通用的使用接口。

class Validator {
  /**
   * 内置校验策略
   */
  static strategy = {
    require(value, errorMsg) {
      if (value === "" || value === undefined || value === null) {
        return errorMsg;
      }
    },
    minLength(value, errorMsg, length) {
      if (value.length < length) {
        return errorMsg;
      }
    },
    maxLength(value, errorMsg, length) {
      if (value.length > length) {
        return errorMsg;
      }
    },
    number(value, errorMsg) {
      if (!/^\d+$/.test(value)) {
        return errorMsg;
      }
    },
  };

  rules = [];

  /**
   * 添加一条校验规则
   * @param {*} value 待校验的值
   * @param {*} validFn 校验方法
   * @param {*} errorMsg 错误信息
   */
  add(value, validFn, errorMsg, ...args) {
    if (typeof validFn === "function") {
      this.rules.push(validFn.bind(this, value, errorMsg, ...args));
    } else {
      console.error("validFn 必须是一个函数");
    }
  }

  /**
   * 启动校验
   */
  valid() {
    let length = this.rules.length;
    while (length--) {
      const rule = this.rules.shift();
      const errorMsg = rule();
      if (errorMsg) {
        return errorMsg;
      }
    }
  }
}

业务逻辑中的使用方式如下:

// 校验逻辑,返回值为错误信息,无返回值表示校验通过
function valid() {
  const validator = new Validator();
  // 给 name 设置校验规则
  validator.add(form.name, Validator.strategy.require, "name 不能为空");
  validator.add(
    form.name,
    Validator.strategy.minLength,
    "name 长度必须大于 2",
    2
  );
  validator.add(
    form.name,
    Validator.strategy.maxLength,
    "name 长度必须小于 4",
    4
  );

  validator.add(form.age, Validator.strategy.require, "age 不能为空");
  validator.add(form.age, Validator.strategy.number, "age 只能为数字");

  // 校验
  return validator.valid();
}

使用策略模式重构表单校验逻辑后,可以通过配置的方式就完成一个表单的校验,这些校验规则也可以更方便的复用。

总结

策略模式的优点是提高代码的灵活性和可复用性。

它的缺点是使用者必须了解所有的策略,才能选择合适的策略。

如果逻辑比较简单,使用 if-else 足以应对,就没必要用策略模式。

参考

《JavaScript 设计模式与开发实践》

Strategy pattern

A Beginner’s Guide to the Strategy Design Pattern

Strategy