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 相关文章推荐
多广告投放代码 推荐
Nov 13 Javascript
JavaScript与函数式编程解释
Apr 27 Javascript
js修改input的type属性问题探讨
Oct 12 Javascript
js实现可兼容IE、FF、Chrome、Opera及Safari的音乐播放器
Feb 11 Javascript
在web中js实现类似excel的表格控件
Sep 01 Javascript
微信小程序 欢迎界面开发的实例详解
Nov 30 Javascript
Vue键盘事件用法总结
Apr 18 Javascript
Vue2仿淘宝实现省市区三级联动
Apr 15 Javascript
vue.js实现会动的简历(包含底部导航功能,编辑功能)
Apr 08 Javascript
通过js实现压缩图片上传功能
Feb 25 Javascript
vue自定义指令和动态路由实现权限控制
Aug 28 Javascript
ES5和ES6中类的区别总结
Dec 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
ThinkPHP模板中判断volist循环的最后一条记录的验证方法
2014/07/01 PHP
php的mkdir()函数创建文件夹比较安全的权限设置方法
2014/07/28 PHP
php获取指定范围内最接近数的方法
2015/06/02 PHP
JavaScript基本对象
2007/01/11 Javascript
javascript下string.format函数补充
2010/08/24 Javascript
jquery键盘事件介绍
2011/01/31 Javascript
jquery下jstree简单应用 - v1.0
2011/04/14 Javascript
jquery中获取元素的几种方式小结
2011/07/05 Javascript
JSON中key动态设置及JSON.parse和JSON.stringify()的区别
2016/12/29 Javascript
JavaScript对象引用与赋值实例详解
2017/03/15 Javascript
js实现图片加载淡入淡出效果
2017/04/07 Javascript
jquery实现一个全局计时器(商城可用)
2017/06/30 jQuery
BackBone及其实例探究_动力节点Java学院整理
2017/07/14 Javascript
详解webpack3如何正确引用并使用jQuery库
2017/08/26 jQuery
javascript 开发之网页兼容各种浏览器
2017/09/28 Javascript
Django与Vue语法的冲突问题完美解决方法
2017/12/14 Javascript
基于百度地图api清除指定覆盖物(Overlay)的方法
2018/01/26 Javascript
JavaScript实现百度搜索框效果
2020/03/26 Javascript
基于JS实现数字动态变化显示效果附源码
2019/07/18 Javascript
Layui动态生成select下拉选择框不显示的解决方法
2019/09/24 Javascript
详解Vue3 Composition API中的提取和重用逻辑
2020/04/29 Javascript
理解Proxy及使用Proxy实现vue数据双向绑定操作
2020/07/18 Javascript
Python并发编程协程(Coroutine)之Gevent详解
2017/12/27 Python
Python实现html转换为pdf报告(生成pdf报告)功能示例
2019/05/04 Python
Pandas中resample方法详解
2019/07/02 Python
Python urllib.request对象案例解析
2020/05/11 Python
python可以用哪些数据库
2020/06/22 Python
CSS3的calc()做响应模式布局的实现方法
2017/09/06 HTML / CSS
有模特经验的简历自我评价
2013/09/19 职场文书
董事长岗位职责
2013/11/30 职场文书
学校师德承诺书
2014/05/23 职场文书
电子商务系毕业生自荐信
2014/05/29 职场文书
禁止高声喧哗的标语
2014/06/11 职场文书
关于感恩的演讲稿500字
2014/08/26 职场文书
语文课外活动总结
2014/08/27 职场文书
Linux安装Docker详细教程
2022/07/07 Servers