美文网首页简友广场
JS设计模式之策略模式

JS设计模式之策略模式

作者: Splendid飞羽 | 来源:发表于2020-12-09 22:58 被阅读0次

    策略模式

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

    主要解决:在有多种算法相似的情况下,使用 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
    

    优点

    • 能减少大量的 if 语句
    • 复用性好

    策略模式重构表单

    正常表单校验

    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; // 阻止表单提交 
        }
    };
    

    相关文章

      网友评论

        本文标题:JS设计模式之策略模式

        本文链接:https://www.haomeiwen.com/subject/moykgktx.html