简友广场

JS设计模式之策略模式

2020-12-09  本文已影响0人  Splendid飞羽

策略模式

定义: 根据不同参数可以命中不同的策略
意图: 定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。

主要解决:在有多种算法相似的情况下,使用 if...else 所带来的复杂和难以维护

JavaScript 中的策略模式

观察如下获取年终奖的 demo, 根据不同的参数(level)获得不同策略方法(规则), 这是策略模式在 JS 比较经典的运用之一。

const strategy = {
  'S': function(salary) {
    return salary * 4
  },
  'A': function(salary) {
    return salary * 3
  },
  'B': function(salary) {
    return salary * 2
  }
}

const calculateBonus = function(level, salary) {
  return strategy[level](salary)
}

calculateBonus('A', 10000) // 30000

使用策略模式重构后

const S = function(salary) {
  return salary * 4
}

const A = function(salary) {
  return salary * 3
}

const B = function(salary) {
  return salary * 2
}

const calculateBonus = function(func, salary) {
  return func(salary)
}

calculateBonus(A, 10000) // 30000

优点

策略模式重构表单

正常表单校验

var registerForm = document.getElementById('registerForm');
registerForm.onsubmit = function () {
    if (registerForm.userName.value === '') {
        alert('用户名不能为空');
        return false;
    }
    if (registerForm.password.value.length < 6) {
        alert('密码长度不能少于 6 位');
        return false;
    }
    if (!/(^1[3|5|8][0-9]{9}$)/.test(registerForm.phoneNumber.value)) {
        alert('手机号码格式不正确');
        return false;
    }
}

不好的点:

registerForm.onsubmit函数比较庞大,包含了很多if - else语句,这些语句需要覆盖所有的校验规则。
registerForm.onsubmit函数缺乏弹性,如果增加了一种新的校验规则,或者想把密码的长度校验从6改成8,我们都必须深入registerForm.onsubmit函数的内部实现,这是违反开放—封闭原则的。
算法的复用性差,如果在程序中增加了另外一个表单,这个表单也需要进行一些类似的校验,那我们很可能将这些校验逻辑复制得漫天遍野。
用策略模式重构表单校验

var strategies = {
    isNonEmpty: function (value, errorMsg) {
        if (value === '') {
            return errorMsg;
        }
    },
    minLength: function (value, length, errorMsg) {
        if (value.length < length) {
            return errorMsg;
        }
    },
    isMobile: function (value, errorMsg) { // 手机号码格式
        if (!/(^1[3|5|8][0-9]{9}$)/.test(value)) {
            return errorMsg;
        }
    }
};

class Validator {
    constructor() {
        this.cache = []; // 保存校验规则
    }

    add(dom, rule, errorMsg) {
        var ary = rule.split(':'); // 把strategy和参数分开(参数在:之后)
        this.cache.push(function () { // 把校验的步骤用空函数包装起来,并且放入 cache
            var strategy = ary.shift(); // 用户挑选的 strategy
            ary.unshift(dom.value); // 把 input 的 value 添加进参数列表
            ary.push(errorMsg); // 把 errorMsg 添加进参数列表
            return strategies[strategy].apply(dom, ary);
        });
    }

    start() {
        for (var i = 0, validatorFunc; validatorFunc = this.cache[i++];) {
            var msg = validatorFunc(); // 开始校验,并取得校验后的返回信息 
            if (msg) { // 如果有确切的返回值,说明校验没有通过
                return msg;
            }
        }
    }
}

var validataFunc = function () {
    var validator = new Validator(); // 创建一个 validator 对象
    /***************添加一些校验规则****************/
    validator.add(registerForm.userName, 'isNonEmpty', '用户名不能为空');
    validator.add(registerForm.password, 'minLength:6', '密码长度不能少于 6 位');
    validator.add(registerForm.phoneNumber, 'isMobile', '手机号码格式不正确');
    var errorMsg = validator.start(); // 获得校验结果
    return errorMsg; // 返回校验结果 
};

var registerForm = document.getElementById('registerForm');
registerForm.onsubmit = function () {
    var errorMsg = validataFunc(); // 如果 errorMsg 有确切的返回值,说明未通过校验
    if (errorMsg) {
        alert(errorMsg);
        return false; // 阻止表单提交 
    }
};
上一篇下一篇

猜你喜欢

热点阅读