欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

javascript设计模式二:策略模式

程序员文章站 2022-07-02 14:18:14
策略模式指定义一系列算法,将它们一个个封装起来。将不变的部分与变化的部分隔开是每个设计模式的主题,策略模式同样如此,策略模式的基础组成: 一个基于策略模式的程序至少要由两部分组成。第一个部分是一组策略类,策略类封装了具体的算法,并负责具体的计算过程。第二个部分是环境类Context,Context接收。客户的请求,随后把请求委托给某一个策略类(发送消息给某个策略对象),要做到这点,说明Cont......

策略模式指定义一系列算法,将它们一个个封装起来。将不变的部分与变化的部分隔开是每个设计模式的主题,策略模式同样如此,

策略模式的基础组成: 一个基于策略模式的程序至少要由两部分组成。第一个部分是一组策略类,策略类封装了具体的算法,并负责具体的计算过程。第二个部分是环境类Context,Context接收。

客户的请求,随后把请求委托给某一个策略类(发送消息给某个策略对象),要做到这点,说明Context中要维持对某个策略对象的引用。

简易表单校验示例DEMO:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        .error {
            color: #ff0000;
            font-size: 12px;
        }

    </style>
</head>
<body>
    <form action="http://www.x111xx.com/register" method="post" id="registerForm">
        <label>
            <input type="text" name="username" >
        </label>
        <label>
            <input type="password" name="password" >
        </label>
        <label>
            <input type="text" name="phoneNumber">
        </label>
        <button>提交</button>
    </form>
</body>
<script>

    //为元素同时设置多个属性方法
    var setAttributes = function(el, attrs){
        for(var key in attrs){
            el.setAttribute(key, attrs[key])
        }
    }

    //策略类
    var strategiesObj = {
        isNonEmpty: function(name, value, errorMsg){
            if(value === ''){
                var errObj = {errName: name, errValue: value, errMsg: errorMsg}
                return errObj
            }
        },
        minLength: function(name, value, length, errorMsg){
            if(value.length < length){
                var errObj = {errName: name, errValue: value, errLength: length, errMsg: errorMsg}
                return errObj
            }
        },
        isMobile: function(name, value, errorMsg){
            if(!/^1[3|4|5|7|8][0-9]{9}$/.test(value)){
                var errObj = {errName: name, errValue: value, errMsg: errorMsg}
                return errObj
            }
        }
    }

    //Context类
    var Validator = function(){
        this.cache = [];     //保存校验规则
        this.errArr = [];
    }

    Validator.prototype.add = function(name, dom, rules){
        var self = this;
        for(var i=0, rule; rule=rules[i++];){
            (function(rule){
                var strategyAry = rule.strategy.split(':');   //把strategy和rule分隔开
                self.cache.push(function(){     //把校验的步骤用空函数包裹起来,并且放入cache中,
                    var strategy = strategyAry.shift(strategyAry);   //用户选择的strategy   shift()方法是删除数组第一个元素,并返回第一个元素的值
                    strategyAry.unshift(name)
                    strategyAry.splice(1, 0, dom.value)   //把input的value添加进参数列表      unshift()方法是向数组的头部添加元素,并返回新数组的长度
                    strategyAry.push(rule.errorMsg)      //把errorMsg添加进参数列表
                    return strategiesObj[strategy].apply(null, strategyAry)
                })
            })(rule)
        }
    }

    Validator.prototype.start = function(){
        var msgArr = [];
        var msgObj = {};
        for(var i=0; this.cache[i]; i++){    //此处的this.cache[i]函数是在this.cache数组中保存的校验规则函数
            var resultArgs = this.cache[i]();   //开始校验,并取得校验后返回信息
            if(resultArgs){    //如果有确切的返回值,说明校验没有通过
                msgArr.push({    //将策略类具体方法的返回值组合成数组对象,包含错误表单的索引、name、value、错误信息及其他自定义配置信息如长度
                    errIndex:i, 
                    errName: resultArgs.errName, 
                    errValue: resultArgs.errValue, 
                    errMsg: resultArgs.errMsg, 
                    errLength: resultArgs.errLength?resultArgs.errLength:''
                })
            }
        }
        this.errArr = msgArr;
        return msgArr
    }

    Validator.prototype.errShow = (function(){
        var singleErr = null;

        return function(){
            console.log(singleErr);
            if(!singleErr){
                for(var i=0, l=this.errArr.length; i<l; i++){
                    var label = document.createElement('label')
                    label.innerHTML = this.errArr[i].errMsg
                    setAttributes(label, {
                        'for': this.errArr[i].errName,
                        'id': this.errArr[i].errName + '-error',
                        'class': 'error'
                    })
                    //原生js插入兄弟节点是方法是获取父节点然后通过appendChild或者insertBefore方法插入
                    document.getElementsByName(this.errArr[i].errName)[0].parentNode.append(label)
                }
            }
            return singleErr = label;
        }
    })()

    //实例化Validator类
    var ValidatorFunc = function(){
        var validator = new Validator();        //创建一个validator对象

        //添加一些校验规则
        validator.add('username', registerForm.username, [{strategy: 'isNonEmpty', errorMsg: '用户名不能为空'}])
        validator.add('password', registerForm.password, [{strategy: 'minLength:6', errorMsg: '密码长度不能少于6位'}])
        validator.add('phoneNumber', registerForm.phoneNumber, [{strategy: 'isMobile', errorMsg: '手机号码格式不正确'}, {strategy: 'minLength:11', errorMsg: '手机号长度不能少于11位'}])

        var errorMsgArr = validator.start();   //获得校验结果
        validator.errShow()
        return errorMsgArr;    //返回校验结果
    }

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

</script>
</html>

这个DEMO还是存在bug的,比如重复点击提交按钮时会出现多次错误提示,这个应该可以通过将实例方法errShow改为类方法加闭包加单例模式来解决。另外在具体调用配置上还有很多可以优化的地方。

通过上面DEMO学习,可以体会到策略模式的优越性,比如可以用在多重条件选择语句上,将条件分支算法封装到策略类中,使得它们易于切换、易于理解,还容易扩展。

喜欢本文请扫下方二维码,关注微信公众号: 前端小二 查看更多我写的文章哦,多谢支持。
javascript设计模式二:策略模式

本文地址:https://blog.csdn.net/qq_34832846/article/details/85988071