策略模式指定义一系列算法,将它们一个个封装起来。将不变的部分与变化的部分隔开是每个设计模式的主题,策略模式同样如此,
策略模式的基础组成: 一个基于策略模式的程序至少要由两部分组成。第一个部分是一组策略类,策略类封装了具体的算法,并负责具体的计算过程。第二个部分是环境类Context,Context接收。
客户的请求,随后把请求委托给某一个策略类(发送消息给某个策略对象),要做到这点,说明Context中要维持对某个策略对象的引用。
简易表单校验示例DEMO:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <style> .error { color: #ff0000; font-size: 12px; } </style> </head> <body> <form action="http://www.x111xx.com/register" method="post" id="registerForm"> <label> <input type="text" name="username" > </label> <label> <input type="password" name="password" > </label> <label> <input type="text" name="phoneNumber"> </label> <button>提交</button> </form> </body> <script> //为元素同时设置多个属性方法 var setAttributes = function(el, attrs){ for(var key in attrs){ el.setAttribute(key, attrs[key]) } } //策略类 var strategiesObj = { isNonEmpty: function(name, value, errorMsg){ if(value === ''){ var errObj = {errName: name, errValue: value, errMsg: errorMsg} return errObj } }, minLength: function(name, value, length, errorMsg){ if(value.length < length){ var errObj = {errName: name, errValue: value, errLength: length, errMsg: errorMsg} return errObj } }, isMobile: function(name, value, errorMsg){ if(!/^1[3|4|5|7|8][0-9]{9}$/.test(value)){ var errObj = {errName: name, errValue: value, errMsg: errorMsg} return errObj } } } //Context类 var Validator = function(){ this.cache = []; //保存校验规则 this.errArr = []; } Validator.prototype.add = function(name, dom, rules){ var self = this; for(var i=0, rule; rule=rules[i++];){ (function(rule){ var strategyAry = rule.strategy.split(':'); //把strategy和rule分隔开 self.cache.push(function(){ //把校验的步骤用空函数包裹起来,并且放入cache中, var strategy = strategyAry.shift(strategyAry); //用户选择的strategy shift()方法是删除数组第一个元素,并返回第一个元素的值 strategyAry.unshift(name) strategyAry.splice(1, 0, dom.value) //把input的value添加进参数列表 unshift()方法是向数组的头部添加元素,并返回新数组的长度 strategyAry.push(rule.errorMsg) //把errorMsg添加进参数列表 return strategiesObj[strategy].apply(null, strategyAry) }) })(rule) } } Validator.prototype.start = function(){ var msgArr = []; var msgObj = {}; for(var i=0; this.cache[i]; i++){ //此处的this.cache[i]函数是在this.cache数组中保存的校验规则函数 var resultArgs = this.cache[i](); //开始校验,并取得校验后返回信息 if(resultArgs){ //如果有确切的返回值,说明校验没有通过 msgArr.push({ //将策略类具体方法的返回值组合成数组对象,包含错误表单的索引、name、value、错误信息及其他自定义配置信息如长度 errIndex:i, errName: resultArgs.errName, errValue: resultArgs.errValue, errMsg: resultArgs.errMsg, errLength: resultArgs.errLength?resultArgs.errLength:'' }) } } this.errArr = msgArr; return msgArr } Validator.prototype.errShow = (function(){ var singleErr = null; return function(){ console.log(singleErr); if(!singleErr){ for(var i=0, l=this.errArr.length; i<l; i++){ var label = document.createElement('label') label.innerHTML = this.errArr[i].errMsg setAttributes(label, { 'for': this.errArr[i].errName, 'id': this.errArr[i].errName + '-error', 'class': 'error' }) //原生js插入兄弟节点是方法是获取父节点然后通过appendChild或者insertBefore方法插入 document.getElementsByName(this.errArr[i].errName)[0].parentNode.append(label) } } return singleErr = label; } })() //实例化Validator类 var ValidatorFunc = function(){ var validator = new Validator(); //创建一个validator对象 //添加一些校验规则 validator.add('username', registerForm.username, [{strategy: 'isNonEmpty', errorMsg: '用户名不能为空'}]) validator.add('password', registerForm.password, [{strategy: 'minLength:6', errorMsg: '密码长度不能少于6位'}]) validator.add('phoneNumber', registerForm.phoneNumber, [{strategy: 'isMobile', errorMsg: '手机号码格式不正确'}, {strategy: 'minLength:11', errorMsg: '手机号长度不能少于11位'}]) var errorMsgArr = validator.start(); //获得校验结果 validator.errShow() return errorMsgArr; //返回校验结果 } var registerForm = document.getElementById('registerForm') registerForm.onsubmit = function(){ var errorMsgArr = ValidatorFunc(); //如果errorMsg有确切的返回值,说明未通过校验 if(errorMsgArr){ console.log(errorMsgArr); return false //阻止表单提交 } } </script> </html>
这个DEMO还是存在bug的,比如重复点击提交按钮时会出现多次错误提示,这个应该可以通过将实例方法errShow改为类方法加闭包加单例模式来解决。另外在具体调用配置上还有很多可以优化的地方。
通过上面DEMO学习,可以体会到策略模式的优越性,比如可以用在多重条件选择语句上,将条件分支算法封装到策略类中,使得它们易于切换、易于理解,还容易扩展。
作者:一期一会
链接:https://blog.csdn.net/qq_34832846/article/details/85988071
来源:CSDN
著作权归作者所有,转载请联系作者获得授权,切勿私自转载。