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去除空格的几种方法
Oct 03 Javascript
jquery $.ajax各个事件执行顺序
Oct 15 Javascript
Javascript的时间戳和php的时间戳转换注意事项
Apr 12 Javascript
Js日期选择器并自动加入到输入框中示例代码
Aug 02 Javascript
jquery快捷动态绑定键盘事件的操作函数代码
Oct 17 Javascript
用原生JS获取CLASS对象(很简单实用)
Oct 15 Javascript
jQuery元素的隐藏与显示实例
Jan 20 Javascript
jQuery找出网页上最高元素的方法
Mar 20 Javascript
纯原生js实现贪吃蛇游戏
Apr 16 Javascript
vue组件watch属性实例讲解
Nov 07 Javascript
JavaScript实现AOP详解(面向切面编程,装饰者模式)
Dec 19 Javascript
React中this丢失的四种解决方法
Mar 12 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数组函数序列之sort() 对数组的元素值进行升序排序
2011/11/02 PHP
理解PHP中的stdClass类
2014/04/18 PHP
php使用GD2绘制几何图形示例
2017/02/15 PHP
js隐藏与显示回到顶部按钮及window.onscroll事件应用
2013/01/25 Javascript
css transform 3D幻灯片特效实现步骤解读
2013/03/27 Javascript
基于jquery实现点击左右按钮图片横向滚动
2013/04/11 Javascript
jquery中文乱码的多种解决方法
2013/06/21 Javascript
javascript获取元素CSS样式代码示例
2013/11/28 Javascript
node.js中的querystring.stringify方法使用说明
2014/12/10 Javascript
使用jquery实现鼠标滑过弹出更多相关信息层附源码下载
2015/11/23 Javascript
jQuery数组处理函数整理
2016/08/03 Javascript
前端MVVM框架解析之双向绑定
2018/01/24 Javascript
layui监听下拉选框选中值变化的方法(包含监听普通下拉选框)
2019/09/24 Javascript
JavaScript封装单向链表的示例代码
2020/09/17 Javascript
python搜索指定目录的方法
2015/04/29 Python
python用Pygal如何生成漂亮的SVG图像详解
2017/02/10 Python
python查询mysql,返回json的实例
2018/03/26 Python
浅谈tensorflow中几个随机函数的用法
2018/07/27 Python
python调用百度语音识别api
2018/08/30 Python
深入浅析Python中的迭代器
2019/06/04 Python
Python 实现数据结构-堆栈和队列的操作方法
2019/07/17 Python
Pyinstaller加密打包应用的示例代码
2020/06/11 Python
使用python实现名片管理系统
2020/06/18 Python
Biblibili视频投稿接口分析并以Python实现自动投稿功能
2021/02/05 Python
Html5跳转到APP指定页面的实现
2020/01/14 HTML / CSS
日本钓鱼渔具和户外用品网上商店:naturum
2016/08/07 全球购物
北美三大旅游网站之一:Travelocity加拿大
2016/08/20 全球购物
宝拉珍选英国官网:Paula’s Choice英国
2019/05/29 全球购物
生产班组长岗位职责
2014/01/05 职场文书
2015年元旦主持词结束语
2014/12/14 职场文书
父亲节活动总结
2015/02/12 职场文书
幼儿园教师节活动总结
2015/03/23 职场文书
同步小康驻村工作简报
2015/07/20 职场文书
2016年离婚协议书范文
2016/03/18 职场文书
Java基础-封装和继承
2021/07/02 Java/Android
关于对TypeScript泛型参数的默认值理解
2022/07/15 Javascript