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 相关文章推荐
jQuery实现随意改变div任意属性的名称和值(部分原生js实现)
May 28 Javascript
javascript密码强度校验代码(两种方法)
Aug 10 Javascript
jQuery插件EasyUI校验规则 validatebox验证框
Nov 29 Javascript
浅谈javascript的call()、apply()、bind()的用法
Feb 21 Javascript
纯css下拉菜单 无需js
Aug 15 Javascript
Vue header组件开发详解
Jan 26 Javascript
详解webpack的proxyTable无效的解决方案
Jun 15 Javascript
jQuery 点击获取验证码按钮及倒计时功能
Sep 20 jQuery
JavaScript对象属性操作实例解析
Feb 04 Javascript
vue.js this.$router.push获取不到params参数问题
Mar 03 Javascript
jquery实现烟花效果(面向对象)
Mar 10 jQuery
Jquery $.map使用方法实例详解
Sep 01 jQuery
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 a simple smtp class
2007/11/26 PHP
jQuery+php实现ajax文件即时上传的详解
2013/06/17 PHP
ThinkPHP提示错误Fatal error: Allowed memory size的解决方法
2015/02/12 PHP
PHP判断是否连接上网络的方法
2015/07/01 PHP
解决FireFox下[使用event很麻烦]的问题
2006/11/26 Javascript
JavaScript异步调用定时方法并停止该方法实现代码
2012/03/16 Javascript
jQuery的attr与prop使用介绍
2013/10/10 Javascript
让新消息在网页标题闪烁提示的jQuery代码
2013/11/04 Javascript
jQuery瀑布流插件Wookmark使用实例
2014/04/02 Javascript
jQuery自定义动画函数实例详解(附demo源码)
2015/12/10 Javascript
基于Javascript实现倒计时功能
2016/02/22 Javascript
js基础之DOM中元素对象的属性方法详解
2016/10/28 Javascript
vue通过watch对input做字数限定的方法
2017/07/13 Javascript
jQuery EasyUI 折叠面板accordion的使用实例(分享)
2017/12/25 jQuery
vue-router路由懒加载的实现(解决vue项目首次加载慢)
2018/08/28 Javascript
bootstrap与pagehelper实现分页效果
2018/12/29 Javascript
vue组件数据传递、父子组件数据获取,slot,router路由功能示例
2019/03/19 Javascript
JavaScript创建、读取和删除cookie
2019/09/03 Javascript
微信小程序实现滑动翻页效果(完整代码)
2019/12/06 Javascript
[33:17]OG vs VGJ.T 2018国际邀请赛小组赛BO2 第二场 8.18
2018/08/19 DOTA
Python中functools模块函数解析
2017/03/12 Python
python使用tornado实现简单爬虫
2018/07/28 Python
Python中使用__new__实现单例模式并解析
2019/06/25 Python
Python中生成一个指定长度的随机字符串实现示例
2019/11/06 Python
Pycharm pyuic5实现将ui文件转为py文件,让UI界面成功显示
2020/04/08 Python
html5图片上传预览示例分享
2014/04/14 HTML / CSS
canvas拼图功能实现代码示例
2018/11/21 HTML / CSS
澳大利亚汽车零部件、音响及配件超市:Automotive Superstore
2018/06/19 全球购物
Guess美国官网:美国知名服装品牌
2019/04/08 全球购物
岗位职责风险防控
2014/02/18 职场文书
2014年征兵标语
2014/06/20 职场文书
贫困证明书格式及范文
2014/10/15 职场文书
如何写贫困证明申请书
2014/10/29 职场文书
医院见习总结
2015/06/24 职场文书
《攀登者》:“海拔8000米以上,你不能指望任何人”
2019/11/25 职场文书
winserver2019安装软件一直卡在应用程序正在为首次使用做准备
2022/06/10 Servers