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高级程序设计 XML、Ajax 学习笔记
Sep 10 Javascript
jQuery在iframe中无法弹出对话框的解决方法
Jan 12 Javascript
推荐10 款 SVG 动画的 JavaScript 库
Mar 24 Javascript
jquery 插件实现瀑布流图片展示实例
Apr 03 Javascript
jQuery学习心得总结(必看篇)
Jun 10 Javascript
Bootstrap零基础入门教程(三)
Jul 18 Javascript
js移动端事件基础及常用事件库详解
Aug 15 Javascript
vueJs实现DOM加载完之后自动下拉到底部的实例代码
Aug 31 Javascript
JavaScript实现学生在线做题计时器功能
Dec 05 Javascript
2019 年编写现代 JavaScript 代码的5个小技巧(小结)
Jan 15 Javascript
vue如何在用户要关闭当前网页时弹出提示的实现
May 31 Javascript
vue pages 多入口项目 + chainWebpack 全局引用缩写说明
Sep 21 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 存储文本换行实现方法
2010/01/05 PHP
PHP使用memcache缓存技术提高响应速度的方法
2014/12/26 PHP
PHP中curl_setopt函数用法实例分析
2015/04/16 PHP
php无序树实现方法
2015/07/28 PHP
Symfony2实现在controller中获取url的方法
2016/03/18 PHP
php微信开发之图片回复功能
2018/06/14 PHP
php获取用户真实IP和防刷机制的实例代码
2018/11/28 PHP
Js保留小数点的4种效果实现代码分享
2014/04/12 Javascript
javascript中解析四则运算表达式的算法和示例
2014/08/11 Javascript
AngularJS中的过滤器filter用法完全解析
2016/04/22 Javascript
jQuery实现可拖拽的许愿墙效果【附demo源码下载】
2016/09/14 Javascript
自学实现angularjs依赖注入
2016/12/20 Javascript
前端开发必知的15个jQuery小技巧
2017/01/22 Javascript
详解Vue组件插槽的使用以及调用组件内的方法
2018/11/13 Javascript
详解单页面路由工程使用微信分享及二次分享解决方案
2019/02/22 Javascript
详解nodejs http请求相关总结
2019/03/31 NodeJs
微信小程序之 catalog 切换实现解析
2019/09/12 Javascript
layui table 多行删除(id获取)的方法
2019/09/12 Javascript
jQuery 选择器用法基础入门示例
2020/01/04 jQuery
原生javascript运动函数的封装示例【匀速、抛物线、多属性的运动等】
2020/02/23 Javascript
python回调函数用法实例分析
2015/05/09 Python
Python将多个list合并为1个list的方法
2018/06/27 Python
python 读写文件,按行修改文件的方法
2018/07/12 Python
Pycharm使用远程linux服务器conda/python环境在本地运行的方法(图解))
2019/12/09 Python
Jupyter notebook无法导入第三方模块的解决方式
2020/04/15 Python
static关键字的用法
2013/10/07 面试题
电焊工工作岗位职责
2014/02/06 职场文书
旅游管理专业大学生职业规划书
2014/02/27 职场文书
经贸专业毕业生求职信范文
2014/05/01 职场文书
医药公司采购员岗位职责
2014/09/12 职场文书
企业法人代表授权委托书
2014/10/02 职场文书
2014年人力资源工作总结
2014/11/19 职场文书
吧主申请感言怎么写
2015/08/03 职场文书
新农村建设指导员工作总结
2015/08/13 职场文书
2016年公司中秋节致辞
2015/11/26 职场文书
漫画「请问您今天要来点兔子吗?」最新杂志彩页公开
2022/03/24 日漫