js 策略模式

xiaoxiao2021-02-28  71

js 策略模式 例如,很多公司的年终奖是根据员工的工资基数和年底绩效情况来发放的.例如,绩效为s的人年终奖有4倍工资, 绩效为A的人年终奖有3倍工资,而绩效为B的人年终奖是2倍工资. 最初思路 编写一个calculateBonus的函数来计算每个人的奖金数额. 这个函数,接受两个参数:员工的工资数额和他的绩效考核等级. var calculateBonus = function(performanceLevel,salary){ if(performanceLevel === 'S'){ return salary * 4; } if(performanceLevel === 'A'){ return salary * 3; } if(performanceLevel === 'B'){ return salary * 2; } } calculateBonus('B',20000); calculateBonus('S',10000);缺点: calculateBonus函数比较庞大,包含了很多的if-else语句,这些语句需要覆盖所有的逻辑分支 calculateBonus函数缺乏弹性,如果再增加一种新的绩效考核,我们必须修改函数内部实现. 这违反了开放-封闭原则 算法的复用性差,如果在程序的其它地方需要重用这些计算奖金的算法,我们的复制过去;使用组合函数重构代码 一般最容易想到的办法就是使用组合函数来重构代码,我们把各种算法封装到一个个的小函数里面, 这些小函数有良好的命名,可以一目了然地知道它对应哪种算法,它们可以复用到程序的其它地方: var performanceS = function(salary){ return salary * 4; } var performanceA = function(salary){ return salary * 3; } var performanceB = function(salary){ return salary * 2; } var calculateBons = function(performanceLevel,salary){ if(performanceLevel === 'S'){ return performanceS(salary) } if(performanceLevel === 'A'){ return performanceA(salary) } if(performanceLevel === 'B'){ return performanceB(salary) } } console.log(calculateBons('A',10000));目前,虽然程序上得到一定程度上的改善,但是这种改善风格有限,问题仍然存在: calculateBons 函数有可能越来越大,而且在系统变化的时候缺乏弹性. 使用策略模式重构代码 策略模式指的是定义一系列的算法,把它们封装起来.目的就是将算法的使用与算法的实现分离出来 一个基于策略模式的程序至少由两部分组成.第一部分是一组策略类,封装了具体的算法,并负责具体的计算过程; 第二部分是环境类Context,Context接受用户的请求,随后把请求委托给某一个策略类. var performanceS = function(){}; performanceS.prototype.calculate = function(salary){ return salary * 4; } var performanceA = function(){}; performanceA.prototype.calculate = function(salary){ return salary * 3; } var performanceB = function(){}; performanceB.prototype.calculate = function(salary){ return salary * 2; } var Bonus = function(){ this.salary = null;//原始工资 this.strategy = null;//绩效等级对应的策略对象 } Bonus.prototype.setSalary = function(salary){ this.salary = salary;// 设置员工的原始工资 } Bonus.prototype.setStrategy = function(strategy){ this.strategy = strategy;//设置员工绩效等级对应的策略对象; } Bonus.prototype.getBonus = function(){// 取得奖金数额 return this.strategy.calculate(this.salary);//把计算奖金的操作委托给对应的策略对象 } var bouns = new Bonus(); bouns.setSalary(10000); bouns.setStrategy(new performanceS());//设置策略对象 console.log(bouns.getBonus());我们让strategy对象从各个策略类中创建而来,这是模拟一些传统面向对象实现. 实际上,在js中,函数也是对象,所以更简单和直接的做法是把strategy直接定义为函数; var strategies = { 'S':function(salary){ return salary * 4; }, 'A':function(salary){ return salary * 3; }, 'B':function(salary){ return salary * 2; } }; /* 同样,Context也没有必要用Bonus类来表示,我们已然用calculateBonus函数充当Context来接受用户的请求 * */ var calculateBonus = function(level,salary){ return strategies[level](salary); }; console.log(calculateBonus('S',20000));接下来,我们用策略模式重构表单验证 var $btn = document.getElementsByTagName('button')[0], $registerForm = document.getElementById('registerForm'); $btn.onclick = function(){ if($registerForm.userName.value === ''){ alert('用户名不能为空'); return false; } if($registerForm.password.value.length < 6){ alert('密码长度不能少于6位'); return false; } if(!/(^1[3|5|8][0-9]{9}$)/.test( $registerForm.phoneNumber.value ) ){ alert('手机号码格式不正确'); return false; } }这是一种常见的代码编写方式,缺点如下" $btn.onclick函数比较庞大,包含了很多的if-else语句,这些语句覆盖了所有的校验规则 $btn.onclick函数缺乏弹性,如果增加了一种新的校验规则,或者想把密码的长度校验从6改成8, 都必须深入函数内部去修改,这是违反了开放-封闭原则; 算法的复用性差,如果在程序中增加了另外一个表单,得复制过去; 用策略模式重构表单校验 很显然第一部要把这些校验逻辑都封装成策略对象: var strategies = { isNonEmpty:function(value,errorMsg){// 不为空 if(value === ''){ return errorMsg; } }, minLength:function(value,length,errorMsg){// 限制最小长度 if(value.length < length){ return errorMsg; } }, isMobile:function(value,errorMsg){// 手机号码格式 if(!/(^1[3|5|8][0-9]{9}$)/.test( value )){ return errorMsg; } } }接下来我们实现Validator类,Validator类在这里我们作为Context,负责接受用户的请求 并委托给strategy对象.在给出Validator类的代码前,有必要提前了解用户是如何向Validator类发送请求的, 有助于我们知道如何去编写Validator类的代码 var validataFunc = function(){ var validator = new Validator();//创建一个validator对象 /**添加一些校验规则**/ validator.add(document.getElementById('userName'),'isNonEmpty','用户名不能为空'); validator.add(document.getElementById('password'),'minLength:10','密码长度不能少于10位'); validator.add(document.getElementById('phoneNumber'),'isMobile','手机号码格式不正确'); var errorMsg = validator.start();//获得校验结果 return errorMsg; }; $btn.onclick = function(){ var errorMsg = validataFunc();// 如何errorMsg有确切的返回值,说明未通过校验 if(errorMsg){ alert(errorMsg); return false;//阻止表单提交 } };可以看到,我们先创建了一个validator对象,然后通过validator.add方法,往validator对象中添加一些校验规则, validator.add方法接受3个参数, 'minLength:6'是一个以冒号隔开的字符串; 当往validator对象里添加完一系列的校验规则之后,会调用validator.start()方法来启动校验. 如果validator.start()返回一个确切的errorMsg字符串当作返回值,说明该校验没通过, 此时阻止表单提交 最后就是Validator类的实现 var Validator = function(){ this.cache = []; //保存校验规则 }; Validator.prototype.add = function( dom, rule, errorMsg ) { var ary = rule.split(':'); //把strategy和参数分开 this.cache.push(function () { //把校验的步骤用空函数包装起来,并且放入cache var strategy = ary.shift();// 用户挑选的strategy ary.unshift(dom.value);//把input的value添加进参数列表 ary.push(errorMsg); //把errorMsg添加参数列表 return strategies[strategy].apply(dom, ary); }); }; Validator.prototype.start = function(){ console.log(this.cache); for ( var i = 0, validatorFunc; validatorFunc = this.cache[ i++ ]; ){ var msg = validatorFunc();// 开始校验,并取得校验后的返回信息 if ( msg ){// 如果有确切的返回值,说明校验没有通过; return msg; } } };
转载请注明原文地址: https://www.6miu.com/read-60926.html

最新回复(0)