JavaScript设计模式之策略模式

什么是策略模式

定义一系列的算法,封装起来,并且他们之间可以相互替换。将算法的使用和算法的实现分离开来。

现在有这么一个功能,过年发年终奖根据绩效来发不同的奖金。
绩效为 S 的有4倍工资,绩效为 A 的有3倍工资,绩效为 B 的有2倍工资。

通过构造函数实现

1
// 算法
2
var ps = function () { }
3
ps.prototype.calculate = function (salary) {
4
    return salary * 4;
5
}
6
7
var pA = function () { }
8
pA.prototype.calculate = function (salary) {
9
    return salary * 3;
10
}
11
12
var pB = function () { }
13
pB.prototype.calculate = function (salary) {
14
    return salary * 2;
15
}
16
17
18
// 算法的使用
19
var Bouns = function () {
20
    this.salary = null;     // 原始工资
21
    this.strategy = null;   // 绩效等级的策略对象
22
}
23
24
// 设置原始工资
25
Bouns.prototype.setSalary = function (salary) {
26
    this.salary = salary;
27
}
28
29
// 设置绩效等级的策略对象
30
Bouns.prototype.setStrategy = function (strategy) {
31
    this.strategy = strategy;
32
}
33
34
Bouns.prototype.getBouns = function () {
35
    return this.strategy.calculate(this.salary)
36
}
37
38
var bouns = new Bouns();
39
bouns.setSalary(1000)
40
bouns.setStrategy(new ps())
41
console.log(bouns.getBouns());  // => 4000

函数对象的方式

1
// 定义策略对象
2
var strategies = {
3
    S: function (salary) {
4
        return salary * 4
5
    },
6
    A: function (salary) {
7
        return salary * 3
8
    },
9
    B: function (salary) {
10
        return salary * 2
11
    }
12
}
13
14
// 定义使用方式
15
var bouns = function (level, salary){
16
    return strategies[level](salary)
17
}
18
19
console.log(bouns('S',1000));  // => 4000
20
console.log(bouns('A',1000))   // => 3000

表单验证

一般表单验证

1
<!DOCTYPE html>
2
<html lang="en">
3
  <head>
4
    <meta charset="UTF-8" />
5
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
6
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
    <title>策略模式</title>
8
  </head>
9
  <body>
10
    <form action="#" method="post" id="form">
11
      请输入用户名:<input type="text" name="username" /><br />
12
      请输入密码:<input type="password" name="password" /><br />
13
      请输入手机号:<input type="text" name="phone" /><br />
14
      <button>提交</button>
15
    </form>
16
    <script>
17
      var form = document.getElementById("form");
18
      form.onsubmit = function () {
19
        if (form.username.value === "") {
20
          alert("用户名不能为空");
21
          return false;
22
        }
23
        if (form.password.value.length < 6) {
24
          alert("密码长度不能少于 6 位");
25
          return false;
26
        }
27
        if (!/(^1[3|5|8][0-9]{9}$)/.test(form.phone.value)) {
28
          alert("手机号码格式不正确");
29
          return false;
30
        }
31
      };
32
    </script>
33
  </body>
34
</html>

缺点:

  • 如果表单继续增加那么代码不断变大,太多的if-else语句来覆盖全部的验证规则
  • 缺乏拓展性,如果新增一种校验规则或者密码的长度变了,那我们就需要对代码进行破坏
  • 复用性太差,其他表单使用那么就要把这一长串校验逻辑复制黏贴

运用策略模式的表单校验

1
<!DOCTYPE html>
2
<html lang="en">
3
  <head>
4
    <meta charset="UTF-8" />
5
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
6
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
    <title>策略模式</title>
8
  </head>
9
  <body>
10
    <form action="#" method="post" id="form">
11
      请输入用户名:<input type="text" name="username" /><br />
12
      请输入密码:<input type="password" name="password" /><br />
13
      请输入手机号:<input type="text" name="phone" /><br />
14
      <button>提交</button>
15
    </form>
16
    <script>
17
      var form = document.getElementById("form");
18
19
      // 定义策略验证规则
20
      var strategies = {
21
        isNotEmpty: function (value, errorMsg) {
22
          if (value == "") {
23
            return errorMsg;
24
          }
25
        },
26
        min: function (value, length, errorMsg) {
27
          if (value.length < length) {
28
            return errorMsg;
29
          }
30
        },
31
        isMobile: function (value, errorMsg) {
32
          if (!/^1[3|5|8][0-9]{9}$/.test(value)) {
33
            return errorMsg;
34
          }
35
        },
36
      };
37
38
      // 验证类
39
      class Validator {
40
        constructor() {
41
          this.cache = []; // 保存校验规则
42
        }
43
44
        add(dom, rule, errorMsg) {
45
          var array = rule.split(":"); // 拆分验证规则与参数
46
          this.cache.push(function () {
47
            var strategy = array.shift(); // 用户选择的验证规则
48
            array.unshift(dom.value); // 验证数据
49
            array.push(errorMsg); // 错误信息
50
            return strategies[strategy](...array);
51
          });
52
        }
53
54
        valid() {
55
          for (var i = 0, validatorFunc; (validatorFunc = this.cache[i++]); ) {
56
            var msg = validatorFunc(); // 开始校验,并取得校验后的返回信息
57
            if (msg) {
58
              // 如果有返回值,说明校验没有通过
59
              return msg;
60
            }
61
          }
62
        }
63
      }
64
65
      // 验证方法
66
      var validateFunc = function () {
67
        var validator = new Validator();
68
        // 添加验证规则
69
        validator.add(form.username, "isNotEmpty", "用户名不能为空");
70
        validator.add(form.password, "min:6", "密码的长度不能小于6位");
71
        validator.add(form.phone, "isMobile", "手机号格式不正确");
72
        // 开启验证
73
        var errorMsg = validator.valid();
74
        return errorMsg;
75
      };
76
77
      form.onsubmit = function () {
78
        var errorMsg = validateFunc();
79
        if (errorMsg) {
80
          alert(errorMsg);
81
          return false;
82
        }
83
      };
84
    </script>
85
  </body>
86
</html>