zoukankan      html  css  js  c++  java
  • javascript设计模式实践之策略模式--输入验证

    策略模式中的策略就是一种算法或者业务规则,将这些策略作为函数进行封装,并向外提供统一的调用执行。

    先定义一个简单的输入表单:

    <!DOCTYPE html>
    <html>
        <head>
            <meta charset="utf-8">
            <style>
                .form{
                    width: 400px;
                    height: 200px;
                    #margin: 0px auto;
                }
    
                .form-item-label{
                    width:100px;
                    text-align: right;
                    float: left;
                }
    
                .form-item-input{
                    float: left;
                }
    
                .form-item{
                    width: 100% ;
                    height: 50px;
                    line-height: 50px;
                }
            </style>
        </head>
        <body>
    
            <div class='form'>
                <div class="form-item">
                    <div class='form-item-label'><span>用户名:</span></div>
                    <div class='form-item-input'><input id='userName' name='用户名' type="text"></div>
                </div>
    
                <div class="form-item" >
                    <div class='form-item-label'><span>密码:</span></div>
                    <div class='form-item-input'><input id='password' name='密码' type="text"></div>
                </div>
    
                <div class="form-item" >
                    <div class='form-item-label'><span>确认密码:</span></div>
                    <div class='form-item-input'><input id='repassword' name='密码确认' type="text"></div>
                </div>
    
                <div class="form-item" >
                    <div class='form-item-label'><span>邮箱:</span></div>
                    <div class='form-item-input'><input id='mail' name='邮箱' type="text" ></div>
                </div>
            </div>
    
            <br>
            <button id='submit' >提交</button>

    <script type='text/javascript' src="../reference/jquery-1.11.3.min.js"></script>
    </body> </html>

    一般在页面上编辑信息后的提交动作中,都需要对输入的信息进行验证,会看到把很多负责check的代码写在提交函数中或者写在一个独立的check函数中。

    比如像下面这样。

                $(document).ready(function(){
                    $('#submit').bind('click', doSubmit);
                });
    
                function doSubmit(){
                    var eleUserName = document.getElementById('userName');
                    if(eleUserName.value === '') {
                        alert('用户名不能为空');
                        return;
                    }
    
                    if(eleUserName.length < 6) {
                        alert('用户名长度不能少于6个字符');
                        return;
                    }
    
                    if(eleUserName.length > 6) {
                        alert('用户名长度不能多于20个字符');
                        return;
                    }
    
                }

    这样的写法功能上肯定能满足要求,但是,会存在几个问题:

    1.如果我要在其他页面上使用,那就要将代码进行复制,所谓的复用就变成了复制,代码会存在大量重复。好一点的会把check代码分类整理封装,单还会存在较多的重复复制。

    2.如果我要增加一个输入验证,那么就要直接修改提交函数,该函数会显的臃肿,并且是破坏“开闭”原则的。

    3.如果修改了提交函数,就要将函数设计的测试全都覆盖一遍,因为,不知道何时就会发生误改或者未知的情况。

    改造步骤:

    1.将每个验证逻辑看成是一个验证策略并封装成每个验证策略函数,函数参数保持一致,可以接受dom元素,被验证的值,错误消息,定制参数。

    2.定义验证器,可将验证策略函数导入,也可以添加。

    3.验证器提供验证方法,用于验证时的调用,其内部调用具体的验证策略函数。

    4.验证调用。

    步骤1.

    把每一个if都看成一种校验的业务规则,把每种业务规则作为一个单独的策略函数,将所有的策略函数封装成一个策略对象。

                var validationStrategies = {
                    isNoEmpty: function(element, errMsg, value) {
                        if(value === '') {
                            return this.buildInvalidObj(element, errMsg, value );
                        }
                    },
    
                    minLength: function(element, errMsg, value, length) {
                        if(value.length < length){
                            return this.buildInvalidObj(element, errMsg, value);
                        }
                    },
    
                    maxLength: function(element, errMsg, value, length) {
                        if(value.length > length){
                            return this.buildInvalidObj(element, errMsg, value);
                        }
                    },
    
                    isMail: function(element, errMsg, value, length) {
                        var reg = /^(w-*.*)+@(w-?)+(.w{2,})+$/;
                        if(!reg.test(value)){
                            return this.buildInvalidObj(element, errMsg, value);
                        }
                    }
                };

    所有函数的参数的前3个都保持一致,而且是必须的,表示被验证的DOM元素,错误消息,被验证的值,第4个开始由函数自身的验证规则决定定制的参数,可有多个参数。

    “buildInvalidObj”方法只是把前3个参数打成一个错误对象进行返回,只要验证不通过就会返回这个错误对象。

    根据依赖倒置原则,高层次的模块不应该依赖于低层次的模块,因此不能让验证的调用方直接使用。

    通过验证器的方式进行封装和抽象。

    步骤2:

    定义验证器,可以将所有验证策略导入其内,也可以单独添加验证策略函数。

                //输入验证器
                function InputValidators(){
                    this.validators = [];
                    this.strategies = {};
                }
    
                //从策略对象导入验证策略函数
                //参数:
                //  strategies: 包含各种策略函数的对象
                InputValidators.prototype.importStrategies = function(strategies) {
                    for(var strategyName in strategies) {
                        this.addValidationStrategy(strategyName, strategies[strategyName]);
                    }
                };
    
                //添加验证策略函数
                //参数:
                //  name: 策略名称
                //  strategy: 策略函数
                InputValidators.prototype.addValidationStrategy = function(name, strategy){
                    this.strategies[name] = strategy;
                };

    步骤3:

    添加验证方法,接受外部调用。

    第一个参数rule,设置成验证规则,比如 "minLength:6",通过下面的代码会生成对具体策略函数的调用,调用会压到缓存中,等待一起调用。

    ":6"表示策略函数根据自身规则所定制的参数。

                //添加验证方法
                //参数:
                //  rule: 验证策略字符串
                //  element: 被验证的dom元素
                //  errMsg: 验证失败时显示的提示信息
                //  value: 被验证的值
                InputValidators.prototype.addValidator = function(rule, element, errMsg, value) {
                    var that = this;
                    var ruleElements = rule.split(":");
    
                    this.validators.push(function() {
                        var strategy = ruleElements.shift();
                        var params = ruleElements;
                        params.unshift(value);
                        params.unshift(errMsg);
                        params.unshift(element);
    
                        return that.strategies[strategy].apply(that, params);
                    });
                };

    通过一个check函数来调用所有的验证。并将错误的结果进行返回。

                //开始验证
                InputValidators.prototype.check = function() {
                    for(var i = 0, validator; validator = this.validators[i++];){
                        var result = validator();
                        if(result) {
                            return result;
                        }
                    }
                };

    步骤4:

    在需要验证的地方,先new一个验证器对象。

                    var validators = new  InputValidators();

    将包含验证策略函数的对象导入,或者单独添加验证策略函数。

                    validators.importStrategies(validationStrategies);
    
                    validators.addValidationStrategy('isEqual',  function(element, errMsg, value1, value2) {
                        if(value1 !== value2) {
                            return this.buildInvalidObj(element, errMsg, value1 );
                        }
                    });

    可以看出,不同的验证策略我们可以预先封装进策略对象中,也可以根据实际情况即时添加。

    然后通过添加验证方法将需要验证的策略,被验证的dom元素,错误消息,被验证的值添加进验证器中,这样避免了直接调用策略对象,降低了耦合性。

    var eleUserName = document.getElementById('userName');
    validators.addValidator('isNoEmpty', eleUserName, '用户名不能为空', eleUserName.value);
    validators.addValidator('minLength:6', eleUserName, '用户名的字符个数必须是6到20个', eleUserName.value);
    validators.addValidator('maxLength:20', eleUserName, '用户名的字符个数必须是6到20个', eleUserName.value);
    
    var elePassword = document.getElementById('password');
    validators.addValidator('isNoEmpty', elePassword, '密码不能为空', elePassword.value);
    validators.addValidator('minLength:6', elePassword, '密码的字符个数必须是6到20个', elePassword.value);
    validators.addValidator('maxLength:20', elePassword, '密码的字符个数必须是6到20个', elePassword.value);
    
    var eleRepassword = document.getElementById('repassword');
    validators.addValidator('isNoEmpty', eleRepassword, '确认密码不能为空', eleRepassword.value);
    validators.addValidator('minLength:6', eleRepassword, '确认密码的字符个数必须是6到20个', eleRepassword.value);
    validators.addValidator('maxLength:20', eleRepassword, '确认密码的字符个数必须是6到20个', eleRepassword.value);
    validators.addValidator('isEqual:' + elePassword.value, eleRepassword, '两次密码不一致', eleRepassword.value);
    
    var eleMail = document.getElementById('mail');
    validators.addValidator('isNoEmpty', eleMail, '邮箱不能为空', eleMail.value);
    validators.addValidator('isMail', eleMail, '邮箱不是一个有效的格式', eleMail.value);

    调用验证器的check执行所有的验证。

                    var result = validators.check();
                    if(result){
                        alert(result.errMsg);
                        result.element.focus();
                        result.element.select();
                        return false;
                    }

    check返回的是错误对象,我们可以在check后通过该对象统一地对DOM元素进行提示性操作,比如设置焦点,选中内容,或者为输入框外部包上一层红色的样式。

    至此,可以看出通过策略模式的改造,输入验证时,我们只需要关心用哪个验证规则,采用什么样的提示性信息即可,不再暴露实现细节,方便调用,方便后续的扩展和组件化。

    全部代码:

    <!DOCTYPE html>
    <html>
        <head>
            <meta charset="utf-8">
            <style>
                .form{
                    width: 400px;
                    height: 200px;
                    #margin: 0px auto;
                }
    
                .form-item-label{
                    width:100px;
                    text-align: right;
                    float: left;
                }
    
                .form-item-input{
                    float: left;
                }
    
                .form-item{
                    width: 100% ;
                    height: 50px;
                    line-height: 50px;
                }
            </style>
        </head>
        <body>
    
            <div class='form'>
                <div class="form-item">
                    <div class='form-item-label'><span>用户名:</span></div>
                    <div class='form-item-input'><input id='userName' name='用户名' type="text"></div>
                </div>
    
                <div class="form-item" >
                    <div class='form-item-label'><span>密码:</span></div>
                    <div class='form-item-input'><input id='password' name='密码' type="text"></div>
                </div>
    
                <div class="form-item" >
                    <div class='form-item-label'><span>确认密码:</span></div>
                    <div class='form-item-input'><input id='repassword' name='密码确认' type="text"></div>
                </div>
    
                <div class="form-item" >
                    <div class='form-item-label'><span>邮箱:</span></div>
                    <div class='form-item-input'><input id='mail' name='邮箱' type="text" ></div>
                </div>
            </div>
    
            <br>
            <button id='submit' >提交</button>
    
    
            <script type='text/javascript' src="../reference/jquery-1.11.3.min.js"></script>
            <script type='text/javascript'>
                $(document).ready(function(){
                    $('#submit').bind('click', doSubmit);
                });
    function doSubmit(){
                    var validators = new  InputValidators();
    
                    validators.importStrategies(validationStrategies);
    
                    validators.addValidationStrategy('isEqual',  function(element, errMsg, value1, value2) {
                        if(value1 !== value2) {
                            return this.buildInvalidObj(element, errMsg, value1 );
                        }
                    });
    
                    var eleUserName = document.getElementById('userName');
                    validators.addValidator('isNoEmpty', eleUserName, '用户名不能为空', eleUserName.value);
                    validators.addValidator('minLength:6', eleUserName, '用户名的字符个数必须是6到20个', eleUserName.value);
                    validators.addValidator('maxLength:20', eleUserName, '用户名的字符个数必须是6到20个', eleUserName.value);
    
                    var elePassword = document.getElementById('password');
                    validators.addValidator('isNoEmpty', elePassword, '密码不能为空', elePassword.value);
                    validators.addValidator('minLength:6', elePassword, '密码的字符个数必须是6到20个', elePassword.value);
                    validators.addValidator('maxLength:20', elePassword, '密码的字符个数必须是6到20个', elePassword.value);
    
                    var eleRepassword = document.getElementById('repassword');
                    validators.addValidator('isNoEmpty', eleRepassword, '确认密码不能为空', eleRepassword.value);
                    validators.addValidator('minLength:6', eleRepassword, '确认密码的字符个数必须是6到20个', eleRepassword.value);
                    validators.addValidator('maxLength:20', eleRepassword, '确认密码的字符个数必须是6到20个', eleRepassword.value);
                    validators.addValidator('isEqual:' + elePassword.value, eleRepassword, '两次密码不一致', eleRepassword.value);
    
                    var eleMail = document.getElementById('mail');
                    validators.addValidator('isNoEmpty', eleMail, '邮箱不能为空', eleMail.value);
                    validators.addValidator('isMail', eleMail, '邮箱不是一个有效的格式', eleMail.value);
    
    
                    var result = validators.check();
                    if(result){
                        alert(result.errMsg);
                        result.element.focus();
                        result.element.select();
                        return false;
                    }
    
                    alert('验证通过');
                }
    
                //输入验证器
                function InputValidators(){
                    this.validators = [];
                    this.strategies = {};
    
                    //this.from(validationStrategies);
                }
    
                //添加验证方法
                //参数:
                //  rule: 验证策略字符串
                //  element: 被验证的dom元素
                //  errMsg: 验证失败时显示的提示信息
                //  value: 被验证的值
                InputValidators.prototype.addValidator = function(rule, element, errMsg, value) {
                    var that = this;
                    var ruleElements = rule.split(":");
    
                    this.validators.push(function() {
                        var strategy = ruleElements.shift();
                        var params = ruleElements;
                        params.unshift(value);
                        params.unshift(errMsg);
                        params.unshift(element);
    
                        return that.strategies[strategy].apply(that, params);
                    });
                };
    
                //添加验证策略函数
                //参数:
                //  name: 策略名称
                //  strategy: 策略函数
                InputValidators.prototype.addValidationStrategy = function(name, strategy){
                    this.strategies[name] = strategy;
                };
    
                //从策略对象导入验证策略函数
                //参数:
                //  strategies: 包含各种策略函数的对象
                InputValidators.prototype.importStrategies = function(strategies) {
                    for(var strategyName in strategies) {
                        this.addValidationStrategy(strategyName, strategies[strategyName]);
                    }
                };
    
                //验证失败时,将相关的错误信息打包返回
                //参数:
                //  element: dom元素
                //   errMsg: 验证失败时的提示消息
                //    value: 被验证的值
                InputValidators.prototype.buildInvalidObj = function(element, errMsg, value){
                    return {
                        'value': value,
                        'element': element,
                        'errMsg': errMsg
                    };
                };
    
                //开始验证
                InputValidators.prototype.check = function() {
                    for(var i = 0, validator; validator = this.validators[i++];){
                        var result = validator();
                        if(result) {
                            return result;
                        }
                    }
                };
    
                //验证策略对象,包含默认的验证策略函数
                var validationStrategies = {
                    isNoEmpty: function(element, errMsg, value) {
                        if(value === '') {
                            return this.buildInvalidObj(element, errMsg, value );
                        }
                    },
    
                    minLength: function(element, errMsg, value, length) {
                        if(value.length < length){
                            return this.buildInvalidObj(element, errMsg, value);
                        }
                    },
    
                    maxLength: function(element, errMsg, value, length) {
                        if(value.length > length){
                            return this.buildInvalidObj(element, errMsg, value);
                        }
                    },
    
                    isMail: function(element, errMsg, value, length) {
                        var reg = /^(w-*.*)+@(w-?)+(.w{2,})+$/;
                        if(!reg.test(value)){
                            return this.buildInvalidObj(element, errMsg, value);
                        }
                    }
                };
            </script>
        </body>
    </html>
  • 相关阅读:
    网络爬虫之Cookies解决
    高性能异步爬虫
    Python csv存储
    pandas ExcelWriter用法及代码示例
    pandas to_excel 用法详解
    pandas read_csv 与 to_csv 方法各参数详解
    pandas read_excel操作
    pandas DataFrame.groupby()所见的各种用法详解
    Pandas 中 DataFrame 基本函数整理
    Python 解析二维码 输出文本
  • 原文地址:https://www.cnblogs.com/kongxianghai/p/4985122.html
Copyright © 2011-2022 走看看