JavaScript设计模式之策略模式实现原理详解


Posted in Javascript onMay 29, 2020

俗话说,条条大路通罗马。在现实生活中,我们可以采用很多方法实现同一个目标。比如我们先定个小目标,先挣它一个亿。我们可以根据具体的实际情况来完成这个目标。

策略模式的定义

定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换。

举个例子:表单校验

在一个Web项目中,注册、登录等功能的实现都离不开表单提交。表单校验也是前端常常需要做的事。假设我们正在编写一个注册的页面,在点击提交按钮之前,有如下几条校验逻辑:

  • 用户名不可为空,不允许以空白字符命名,用户名长度不能小于2位。
  • 密码长度不能小于6位。
  • 正确的手机号码格式。

也许,一开始我们会这么写:

<html>
<head>
  <title>策略模式-校验表单</title>
  <meta content="text/html; charset=utf-8" http-equiv="Content-Type">
</head>
<body>
  <form id = "registerForm" method="post" action="http://xxxx.com/api/register">
    用户名:<input type="text" name="userName">
    密码:<input type="text" name="password">
    手机号码:<input type="text" name="phoneNumber">
    <button type="submit">提交</button>
  </form>
  <script type="text/javascript">
    var registerForm = document.getElementById('registerForm');
    registerForm.onsubmit = function() {
      if (registerForm.userName.value === '') {
        alert('用户名不可为空');
        return false;
      }
      if (registerForm.userName.value === '') {
        alert('用户名不可为空');
        return false;
      } 
      if (registerForm.userName.value.trim() === '') {
        alert('用户名不允许以空白字符命名');
        return false;
      } 
      if (registerForm.userName.value.trim().length < 2) {
        alert('用户名用户名长度不能小于2位');
        return false;
      } 
      if (registerForm.password.value.trim().length < 6) {
        alert('密码长度不能小于6位');
        return false;
      }
      if (!/^(13[0-9]|14[5|7]|15[0|1|2|3|5|6|7|8|9]|17[7]|18[0|1|2|3|5|6|7|8|9])\d{8}$/.test(registerForm.phoneNumber.value)) {
        alert('请输入正确的手机号码格式');
        return errorMsg;
       } 
    }
  </script>
</body>
</html>

这是一种很常见的编码方式,但它有很明显的缺点:

  • registerForm.onsubmit 函数比较庞大,包含了很多if语句,这些语句要覆盖所有的校验规则。
  • 若校验规则有变,不得不深入到registerForm.onsubmit 函数的内部实现,违反开放-封闭原则。
  • 算法的复用性差。

下面,让我们来用策略模式重构表单校验

策略模式:表单校验

<html>
<head>
  <title>策略模式-校验表单</title>
  <meta content="text/html; charset=utf-8" http-equiv="Content-Type">
</head>
<body>
  <form id = "registerForm" method="post" action="http://xxxx.com/api/register">
    用户名:<input type="text" name="userName">
    密码:<input type="text" name="password">
    手机号码:<input type="text" name="phoneNumber">
    <button type="submit">提交</button>
  </form>
  <script type="text/javascript">
    // 策略对象
    var strategies = {
      isNoEmpty: function (value, errorMsg) {
        if (value === '') {
          return errorMsg;
        }
      },
      isNoSpace: function (value, errorMsg) {
        if (value.trim() === '') {
          return errorMsg;
        }
      },
      minLength: function (value, length, errorMsg) {
        if (value.trim().length < length) {
          return errorMsg;
        }
      },
      maxLength: function (value, length, errorMsg) {
        if (value.length > length) {
          return errorMsg;
        }
      },
      isMobile: function (value, errorMsg) {
        if (!/^(13[0-9]|14[5|7]|15[0|1|2|3|5|6|7|8|9]|17[7]|18[0|1|2|3|5|6|7|8|9])\d{8}$/.test(value)) {
          return errorMsg;
        }        
      }
    }

    // 验证类
    var Validator = function() {
      this.cache = [];
    }
    Validator.prototype.add = function(dom, rules) {
      var self = this;
      for(var i = 0, rule; rule = rules[i++];) {
        (function(rule) {
          var strategyAry = rule.strategy.split(':');
          var errorMsg = rule.errorMsg;
          self.cache.push(function() {
          var strategy = strategyAry.shift();
          strategyAry.unshift(dom.value);
          strategyAry.push(errorMsg);
          return strategies[strategy].apply(dom, strategyAry);
          })
        })(rule)
      }
    };
    Validator.prototype.start = function() {
      for(var i = 0, validatorFunc; validatorFunc = this.cache[i++];) {
        var errorMsg = validatorFunc();
        if (errorMsg) {
          return errorMsg;
        }
      }
    };

    // 调用代码
    var registerForm = document.getElementById('registerForm');

    var validataFunc = function() {
      var validator = new Validator();
      validator.add(registerForm.userName, [{
        strategy: 'isNoEmpty',
        errorMsg: '用户名不可为空'
      }, {
        strategy: 'isNoSpace',
        errorMsg: '不允许以空白字符命名'
      }, {
        strategy: 'minLength:2',
        errorMsg: '用户名长度不能小于2位'
      }]);
      validator.add(registerForm.password, [ {
        strategy: 'minLength:6',
        errorMsg: '密码长度不能小于6位'
      }]);
      validator.add(registerForm.phoneNumber, [{
        strategy: 'isMobile',
        errorMsg: '请输入正确的手机号码格式'
      }]);
      var errorMsg = validator.start();
      return errorMsg;
    }

    registerForm.onsubmit = function() {
      var errorMsg = validataFunc();
      if (errorMsg) {
        alert(errorMsg);
        return false;
      }
    }
  </script>
</body>
</html>

策略模式优缺点

策略模式是一种常用且有效的设计模式。

  • 策略模式可以有效避免多重条件选择语句。
  • 策略模式提供了对开放-封装原则的完美支持,将方法封装在独立的strategy中,使得它们易于切换,易于理解,易于扩展。
  • 复用性高。

当然,策略模式也有一些缺点

  • 增加了许多策略类或者策略对象。
  • 要使用策略模式,必须了解所有的strategy,违反了最少知识原则。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
JS 实现双色表格实现代码
Nov 24 Javascript
javascript 用原型继承来实现对象系统
Mar 22 Javascript
SlideView 图片滑动(扩展/收缩)展示效果
Aug 01 Javascript
使用 JScript 创建 .exe 或 .dll 文件的方法
Jul 13 Javascript
jQuery实时显示鼠标指针位置和键盘ASCII码
Mar 28 Javascript
js获取客户端操作系统类型的方法【测试可用】
May 27 Javascript
JavaScript计算器网页版实现代码分享
Jul 15 Javascript
Vue项目webpack打包部署到服务器的实例详解
Jul 17 Javascript
vue vantUI实现文件(图片、文档、视频、音频)上传(多文件)
Oct 15 Javascript
Vue实现点击按钮复制文本内容的例子
Nov 09 Javascript
JavaScript对象属性操作实例解析
Feb 04 Javascript
typescript编写微信小程序创建项目的方法
Jan 29 Javascript
JavaScript隐式类型转换代码实例
May 29 #Javascript
vue实现编辑器键盘抬起时内容跟随光标距顶位置向上滚动效果
May 28 #Javascript
node+vue实现文件上传功能
May 28 #Javascript
vue中实现图片压缩 file文件的方法
May 28 #Javascript
详解Vue 数据更新了但页面没有更新的 7 种情况汇总及延伸总结
May 28 #Javascript
Vue实现附件上传功能
May 28 #Javascript
如何使用Javascript中的this关键字
May 28 #Javascript
You might like
PHP网页游戏学习之Xnova(ogame)源码解读(九)
2014/06/24 PHP
PHP常用处理静态操作类
2015/04/03 PHP
用js做一个小游戏平台 (一)
2009/12/29 Javascript
jQuery 表单验证扩展(四)
2010/10/20 Javascript
鼠标滑上去后图片放大浮出效果的js代码
2011/05/28 Javascript
终于解决了IE8不支持数组的indexOf方法
2013/04/03 Javascript
JS清除IE浏览器缓存的方法
2013/07/26 Javascript
jquery默认校验规则整理
2014/03/24 Javascript
JavaScript中的prototype和constructor简明总结
2014/04/05 Javascript
jQuery 如何先创建、再修改、后添加DOM元素
2014/05/20 Javascript
js+CSS实现弹出居中背景半透明div层的方法
2015/02/26 Javascript
JavaScript分页功能的实现方法
2015/04/25 Javascript
iframe中子父类窗口调用JS的方法及注意事项
2015/08/25 Javascript
Parcel.js + Vue 2.x 极速零配置打包体验教程
2017/12/24 Javascript
浅析vue中的MVVM实现原理
2019/03/04 Javascript
React生命周期原理与用法踩坑笔记
2020/04/28 Javascript
vue打包静态资源后显示空白及static文件路径报错的解决
2020/09/02 Javascript
ant design vue导航菜单与路由配置操作
2020/10/28 Javascript
ES6的循环与可迭代对象示例详解
2021/01/31 Javascript
Python 中的 else详解
2016/04/23 Python
python实现应用程序在右键菜单中添加打开方式功能
2017/01/09 Python
Mac 上切换Python多版本
2017/06/17 Python
破解安装Pycharm的方法
2018/10/19 Python
python实现可变变量名方法详解
2019/07/01 Python
使用Django搭建一个基金模拟交易系统教程
2019/11/18 Python
Pycharm生成可执行文件.exe的实现方法
2020/06/02 Python
解决c++调用python中文乱码问题
2020/07/29 Python
Snapfish英国:在线照片打印和个性化照片礼品
2017/01/13 全球购物
面向对象设计的原则是什么
2013/02/13 面试题
化妆品店促销方案
2014/02/24 职场文书
会计自荐信范文
2014/03/09 职场文书
七匹狼男装广告词
2014/03/21 职场文书
2014年教师批评与自我批评思想汇报
2014/09/20 职场文书
幼师个人总结范文
2015/02/28 职场文书
解决Golang中ResponseWriter的一个坑
2021/04/27 Golang
MySQL高级进阶sql语句总结大全
2022/03/16 MySQL