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 相关文章推荐
仿163填写邮件地址自动显示下拉(无优化)
Nov 05 Javascript
Javascript下判断是否为闰年的Datetime包
Oct 26 Javascript
jquery 实现checkbox全选,反选,全不选等功能代码(奇数)
Oct 24 Javascript
Raphael一个用于在网页中绘制矢量图形的Javascript库
Jan 08 Javascript
jquery 循环显示div的示例代码
Oct 18 Javascript
JavaScript中使用Substring删除字符串最后一个字符
Nov 03 Javascript
JS+DIV实现鼠标划过切换层效果的实例代码
Nov 26 Javascript
jQuery autoComplete插件两种使用方式及动态改变参数值的方法详解
Oct 24 Javascript
js 中获取制定的cook信息实现方法
Nov 19 Javascript
js Canvas绘制圆形时钟教程
Feb 06 Javascript
Vue.js 中的实用工具方法【推荐】
Jul 04 Javascript
Vue——前端生成二维码的示例
Dec 19 Vue.js
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做的端口嗅探器--可以指定网站和端口
2006/10/09 PHP
推荐一款MAC OS X 下php集成开发环境mamp
2014/11/08 PHP
codeigniter中view通过循环显示数组数据的方法
2015/03/20 PHP
PHP中addcslashes与stripcslashes函数用法分析
2016/01/07 PHP
Laravel框架创建路由的方法详解
2019/09/04 PHP
一个js写的日历(代码部分网摘)
2009/09/20 Javascript
模仿JQuery sortable效果 代码有错但值得看看
2009/11/05 Javascript
js模仿html5 placeholder适应于不支持的浏览器
2013/01/13 Javascript
js 数值转换为3位逗号分隔的示例代码
2014/02/19 Javascript
javascript中使用正则计算中文长度的例子
2014/04/29 Javascript
javaScript 事件绑定、事件冒泡、事件捕获和事件执行顺序整理总结
2016/10/10 Javascript
js Canvas绘制圆形时钟效果
2017/02/17 Javascript
BootStrap表单宽度设置方法
2017/03/10 Javascript
VUE利用vuex模拟实现新闻点赞功能实例
2017/06/28 Javascript
JS设计模式之惰性模式(二)
2017/09/29 Javascript
JS实现点击循环切换显示内容的方法
2017/10/19 Javascript
使用vue2实现购物车和地址选配功能
2018/03/29 Javascript
vue构建动态表单的方法示例
2018/09/22 Javascript
nodejs一个简单的文件服务器的创建方法
2019/09/13 NodeJs
vue-cli3.X快速创建项目的方法步骤
2019/11/14 Javascript
vue+element实现动态加载表单
2020/12/13 Vue.js
Python中django学习心得
2017/12/06 Python
python爬虫爬取某站上海租房图片
2018/02/04 Python
python创建文件时去掉非法字符的方法
2018/10/31 Python
python实现创建新列表和新字典,并使元素及键值对全部变成小写
2019/01/15 Python
Python一个简单的通信程序(客户端 服务器)
2019/03/06 Python
Python3 sys.argv[ ]用法详解
2019/10/24 Python
python 制作简单的音乐播放器
2020/11/25 Python
python中zip()函数遍历多个列表方法
2021/02/18 Python
html5 Canvas画图教程(10)—把面拆成线条模拟出圆角矩形
2013/01/09 HTML / CSS
Bonprix法国:时尚、鞋子、家居
2020/12/29 全球购物
2014年党课学习心得体会
2014/07/08 职场文书
考试没考好检讨书(精选篇)
2014/11/16 职场文书
个人更名证明
2015/06/23 职场文书
WINDOWS 64位 下安装配置mysql8.0.25最详细的教程
2022/03/22 MySQL
Python内置包对JSON文件数据进行编码和解码
2022/04/12 Python