JavaScript设计模式:基于策略模式的表单输入验证实现

作者:异步漫步

时间:2026-10-29

浏览:150

本文详解如何利用JavaScript策略模式重构表单输入验证逻辑。通过封装独立的验证策略函数,定义统一的验证器接口,有效解决了传统验证代码重复、耦合度高及违反开闭原则的问题。文章提供了从原始实现到策略模式重构的完整步骤与代码示例,帮助开发者提升代码的可维护性与扩展性。

文章目录

策略模式概述

策略模式的核心在于定义一系列算法,将它们分别封装起来,并实现相互替换。这种设计使得算法本身的演变与使用算法的客户代码解耦,保持独立变化。

首先构建一个基础的输入表单结构。


<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-">
    <style>
      .form{
        width: px;
        height: px;
        #margin: px auto;
      }
      .form-item-label{
        width:px;
        text-align: right;
        float: left;
      }
      .form-item-input{
        float: left;
      }
      .form-item{
        width: % ;
        height: px;
        line-height: px;
      }
    </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-...min.js"></script>
  </body>
</html>

在网页开发中,用户提交编辑信息时通常需要进行数据校验。常见的做法是将复杂的校验逻辑直接编写在提交函数内部,或者封装在一个独立的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;
        }
      }

传统验证方式的问题

这种写法虽然在功能上能够满足基本需求,但存在几个显著缺陷:

第一,若其他页面需要复用该逻辑,只能复制代码,导致“复用”实质变为“复制”,产生大量冗余代码。即便对check代码进行分类整理和封装,仍难以避免重复复制的问题。

第二,若需新增输入验证规则,必须直接修改提交函数。这会导致函数体积膨胀,且违背了“开闭原则”(对扩展开放,对修改关闭)。

第三,修改提交函数后,必须重新覆盖所有相关测试用例。由于无法预知修改是否引入未知错误,测试成本随之增加。

基于策略模式的改造方案

针对上述问题,改造步骤如下:

第一,将每个验证逻辑视为独立的验证策略,并封装为对应的策略函数。所有策略函数的参数保持一致,能够接收DOM元素、待验证值、错误消息以及定制参数。

第二,定义一个验证器,用于导入或添加已有的验证策略函数。

第三,验证器提供统一的验证接口,内部负责调用具体的策略函数。

第四,执行验证调用。

封装验证策略

步骤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);
          }
        }
      };

所有函数的参数前三个保持一致且为必填项,分别代表被验证的DOM元素、错误消息以及被验证的值。从第四个参数开始,由具体函数的验证规则决定所需的定制参数,数量不限。

“buildInvalidObj”方法的作用是将前三个参数打包成一个错误对象并返回。只要验证未通过,就会返回该错误对象。

依据依赖倒置原则,高层模块不应依赖低层模块,因此验证的调用方不应直接使用这些底层策略函数。

需要通过验证器进行封装和抽象处理。

定义验证器

步骤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的具体实现如下。

在需要验证的地方,首先实例化一个验证器对象。


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>

以上所述是小编给大家介绍的javascript设计模式--策略模式之输入验证的全部内容,希望大家喜欢。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章