一、策略模式定义:
定义一些列的算法/规则,将它们封装起来,使得它们可以互相替换/组合使用。其目的在于将算法/规则封装起来,将算法/规则的使用与实现分离出来。
通过策略模式,可以减少算法计算过程中大量的if-else分支,并提高复用性。
一个策略模式的程序至少由两部分组成,一个是一组策略类,策略类封装了具体算法,并负责具体的实现过程;第二个部分是环境类context,context接受客户的请求,随后将请求委托给具体的某一个策略类。context中需要有一个变量来保存对对象的引用。
二、java中的策略模式:
考虑以下应用场景,大部分公司都会根据绩效发放年终奖,假设说如果绩效为A的话,年终奖发放5倍月薪;如果绩效为B的话,年终奖发放4倍月薪。按照一般的思路,计算函数中可能包含如下的分支语句,if (绩效为A) else if(绩效为B){};如果新增了一个C绩效,则需要进入计算函数的内部进行修改,因此考虑将算法的实现与算法的使用相分离。
为了实现多态,需要定义对应的接口或者抽象类:
package com.bobo.shejimoshi; public abstract class Performance { public abstract int calBonus(int salary); }
绩效A类:
package com.bobo.shejimoshi; public class PerformanceA extends Performance{ @Override public int calBonus(int salary) { // TODO Auto-generated method stub return salary*4; } }
绩效B类:
package com.bobo.shejimoshi; public class PerformanceB extends Performance{ @Override public int calBonus(int salary) { // TODO Auto-generated method stub return salary*5; } }
实现context类:
package com.bobo.shejimoshi; public class Bonus { private Performance perfo; private int salary; public void setPerfo(Performance perfo) { this.perfo = perfo; } public void setSalary(int salary) { this.salary = salary; } public int getBonus(int salary){ return this.perfo.calBonus(salary); } }
调用方法如下:
package com.bobo.shejimoshi; public class Test { public static void main(String[] args) { Bonus bonus = new Bonus(); bonus.setPerfo(new PerformanceA()); System.out.println(bonus.getBonus(1000));//输出4000 bonus.setPerfo(new PerformanceB()); System.out.println(bonus.getBonus(1000));//输出5000 } }
三、javascript中的策略模式
在javascript中,函数也是对象,因此完全不必要使用策略类,将其直接定义为函数即可。对策略的引用可以像java中通过context类的一个属性来保存其引用,可以通过函数参数来进行传递。
//策略模式 //策略实现 var strategies={ 'S':function(salary){ return salary*4; }, 'A':function(salary){ return salary*3; }, 'B':function(salary){ return salary*2; } }; //context实现 var calBonus=function(leval,salary){ return strategies[leval].call(this,salary); }; console.log(calBonus('S',2000)); console.log(calBonus('A',2000));
三、案例
1)jquery中的动画类,其各种缓动动画,也是策略模式的具体实现;
2)如果认为策略模式就是用来封装算法的,那未免太狭隘了,事实上,广义的算法也可以是一系列的“业务规则”,只要这些业务规则指向的目标一致,就可以被替换,甚至可以被组合使用。如下面封装的一个表单验证插件。
//使用策略模式来实现表单验证 var strategies = { 'isNonEmpty': function(value, errorMsg) { if (value == '') { return errorMsg; } }, 'minLength': function(value, length, errorMsg) { if (value.length < length) { return errorMsg; } }, }; function Validate() { this.cache = []; } Validate.prototype.add = function(dom, rules) { var self = this; for (var i = 0; i < rules.length; i++) { var curRule = rules[i]; (function(curRule) { var argsAry = curRule['strategy'].split(':'); var errorMsg = curRule['errorMsg']; self.cache.push(function() { var strategy = argsAry.shift(); argsAry.unshift(dom.value); argsAry.push(errorMsg); return strategies[strategy].apply(dom, argsAry); }); })(curRule); } }; Validate.prototype.start = function() { for (var i = 0; i < this.cache.length; i++) { var func = this.cache[i]; var errorMsg = func(); if (errorMsg) { return errorMsg; } } }; var testForm = document.getElementById("testForm"); function validateForm() { var validate = new Validate(); validate.add(testForm.username, [{ 'strategy': 'isNonEmpty', 'errorMsg': '该字段不能为空!' }]); validate.add(testForm.password, [{ 'strategy': 'minLength:3', 'errorMsg': '输入长度不能小于3' }]); console.log(validate.cache); var errorMsg = validate.start(); return errorMsg; } testForm.onsubmit = function() { var errorMsg = validateForm(); if (errorMsg) { console.log(errorMsg); return false; } };
如果采用《你不知道的javascript》中推崇的基于委托的写法,那么实现的代码如下:
//基于委托的表单验证组件的实现 //在编写组件之前,首先了解用户是如何使用组件的,总的来说,有两个API //.add(registerForm.password,[{strategy:'minLength:6',errorMsg:'长度不能小于6'},{strategy:'isNonEmpty',errorMsg:'输入不能为空'}],'长度不能小于6位');添加校验规则 //.start()启动校验,返回错误信息 var strategies={ isNonEmpty:function(value,errorMsg){ console.log(arguments); 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; } } }; var validator={ //不是函数,内部不能定义变量 //var cache=[], init:function(){ this.cache=[]; }, add:function(elem,rules){ var i,len,self=this; for(i=0,len=rules.length;i<len;i++){ (function(i){ self.cache.push(function(){ //这是个闭包 var curRule=rules[i], strategyAry=curRule.strategy.split(':'), errorMsg=curRule.errorMsg, strategy=strategyAry.shift(); strategyAry.push(errorMsg); strategyAry.unshift(elem.value); //console.log(strategy); return strategies[strategy].apply(elem,strategyAry); }); })(i); } }, start:function(){ var i,len,errorMsg; for(i=0,len=this.cache.length;i<len;i++){ if((errorMsg=this.cache[i]())!==undefined){ return errorMsg; } } } }; //测试 var form=document.forms['login']; var myValid=Object.create(validator); myValid.init(); myValid.add(form.name,[{strategy:'isNonEmpty',errorMsg:'字段不能为空'},{strategy:'minLength:3',errorMsg:'长度不能小于3'}]); console.log(myValid.start());