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 相关文章推荐
非常不错的功能强大代码简单的管理菜单美化版
Jul 09 Javascript
js parseInt(&quot;08&quot;)未指定进位制问题
Jun 19 Javascript
JavaScript利用Date实现简单的倒计时实例
Jan 12 Javascript
利用n 升级工具升级Node.js版本及在mac环境下的坑
Feb 15 Javascript
利用node.js制作命令行工具方法教程(一)
Jun 22 Javascript
JavaScript for循环 if判断语句(学习笔记)
Oct 11 Javascript
十分钟带你快速了解React16新特性
Nov 10 Javascript
JS实现全屏预览F11功能的示例代码
Jul 23 Javascript
原生JS封装_new函数实现new关键字的功能
Aug 12 Javascript
Angular之jwt令牌身份验证的实现
Feb 14 Javascript
微信小程序实现通讯录列表展开收起
Nov 18 Javascript
使用webpack和rollup打包组件库的方法
Feb 25 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性能优化准备篇图解PEAR安装
2011/12/05 PHP
利用php实现禁用IE和火狐的缓存问题
2012/12/03 PHP
php可应用于面包屑导航的迭代寻找家谱树实现方法
2015/02/02 PHP
用JS实现一个页面多个css样式实现
2008/05/29 Javascript
最佳JS代码编写的14条技巧
2011/01/09 Javascript
JQuery与Ajax调用新浪API获取短网址的代码
2014/02/07 Javascript
node.js中的fs.futimes方法使用说明
2014/12/17 Javascript
JavaScript使用pop方法移除数组最后一个元素用法实例
2015/04/06 Javascript
NODE.JS跨域问题的完美解决方案
2016/10/20 Javascript
js实现三级联动效果(简单易懂)
2017/03/27 Javascript
详解vue-cli中的ESlint配置文件eslintrc.js
2017/09/25 Javascript
详解Chai.js断言库API中文文档
2018/01/31 Javascript
解决vue点击控制单个样式的问题
2018/09/05 Javascript
jQuery实现动画、消失、显现、渐出、渐入效果示例
2018/09/06 jQuery
node.js中ws模块创建服务端和客户端,网页WebSocket客户端
2019/03/06 Javascript
如何利用Node.js与JSON搭建简单的动态服务器
2020/06/16 Javascript
浅要分析Python程序与C程序的结合使用
2015/04/07 Python
Python的条件语句与运算符优先级详解
2015/10/13 Python
Python实现将16进制字符串转化为ascii字符的方法分析
2017/07/21 Python
Python反射的用法实例分析
2018/02/11 Python
使用pandas中的DataFrame数据绘制柱状图的方法
2018/04/10 Python
Python实现合并同一个文件夹下所有PDF文件的方法示例
2018/04/28 Python
Python开发网站目录扫描器的实现
2019/02/21 Python
bluepy 一款python封装的BLE利器简单介绍
2019/06/25 Python
详解Python打包分发工具setuptools
2019/08/05 Python
Carolina工作鞋官网:Carolina Footwear
2019/03/14 全球购物
澳大利亚在线消费电子产品商店:TobyDeals
2020/01/05 全球购物
劳资协议书范本
2014/04/23 职场文书
全国优秀教师事迹材料
2014/08/26 职场文书
科学发展观活动总结
2014/08/28 职场文书
民警个人对照检查剖析材料
2014/09/17 职场文书
法人单位授权委托书范文
2014/10/06 职场文书
教师旷工检讨书
2015/08/15 职场文书
写一个Python脚本自动爬取Bilibili小视频
2021/04/24 Python
python for循环赋值问题
2021/06/03 Python
Python简易开发之制作计算器
2022/04/28 Python