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 相关文章推荐
详解JavaScript函数绑定
Aug 18 Javascript
使用jquery局部刷新(jquery.load)从数据库取出数据
Jan 22 Javascript
js+html5操作sqlite数据库的方法
Feb 02 Javascript
javascript显示上周、上个月日期的处理方法
Feb 03 Javascript
jQuery回到顶部的代码
Jul 09 Javascript
vue-cli启动本地服务局域网不能访问的原因分析
Jan 22 Javascript
几个你不知道的技巧助你写出更优雅的vue.js代码
Jun 11 Javascript
vue 刷新之后 嵌套路由不变 重新渲染页面的方法
Sep 13 Javascript
JS数组去重的6种方法完整实例
Dec 08 Javascript
JS中实现一个下载进度条及播放进度条的代码
Jun 10 Javascript
浅谈vue3中effect与computed的亲密关系
Oct 10 Javascript
vue自动添加浏览器兼容前后缀操作
Aug 13 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的配置文件php.ini
2006/10/09 PHP
destoon利用Rewrite规则设置网站安全
2014/06/21 PHP
PHP易混淆知识整理笔记
2015/09/24 PHP
php实现带读写分离功能的MySQL类完整实例
2016/07/28 PHP
PHP云打印类完整示例
2016/10/15 PHP
PHP流Streams、包装器wrapper概念与用法实例详解
2017/11/17 PHP
PHP如何使用JWT做Api接口身份认证的实现
2020/02/03 PHP
js 实现无干扰阴影效果 简单好用(附文件下载)
2009/12/27 Javascript
超级酷和最实用的jQuery实例收集(20个)
2010/04/21 Javascript
基于JavaScript实现 获取鼠标点击位置坐标的方法
2013/04/12 Javascript
网页禁用右键菜单和鼠标拖动选择方法小结
2015/02/25 Javascript
jQuery Ajax使用实例
2015/04/16 Javascript
深入探讨javascript函数式编程
2015/10/11 Javascript
详解node.js平台下Express的session与cookie模块包的配置
2017/04/26 Javascript
Angular 表单控件示例代码
2017/06/26 Javascript
Angular4 中内置指令的基本用法
2017/07/31 Javascript
node.js 用socket实现聊天的示例代码
2017/10/17 Javascript
Node.js使用MySQL连接池的方法实例
2018/02/11 Javascript
vue-cli项目中使用echarts图表实例
2018/10/22 Javascript
vue+element模态框中新增模态框和删除功能
2019/06/11 Javascript
利用python实现微信头像加红色数字功能
2018/03/26 Python
Python实现自定义顺序、排列写入数据到Excel的方法
2018/04/23 Python
python事件驱动event实现详解
2018/11/21 Python
详解【python】str与json类型转换
2019/04/29 Python
python 使用pdfminer3k 读取PDF文档的例子
2019/08/27 Python
python多继承(钻石继承)问题和解决方法简单示例
2019/10/21 Python
Pycharm生成可执行文件.exe的实现方法
2020/06/02 Python
python判断是空的实例分享
2020/07/06 Python
获取CSDN文章内容并转换为markdown文本的python
2020/09/06 Python
css3动画效果小结(推荐)
2016/07/25 HTML / CSS
HTML5中新标签和常用标签详解
2014/03/07 HTML / CSS
Html5 FileReader实现即时上传图片功能实例代码
2014/09/01 HTML / CSS
深入浅析HTML5中的SVG
2015/11/27 HTML / CSS
说谎欺骗人检讨书300字
2014/11/18 职场文书
IDEA2021.2配置docker如何将springboot项目打成镜像一键发布部署
2021/09/25 Java/Android
Python保存并浏览用户的历史记录
2022/04/29 Python