学习JavaScript设计模式(策略模式)
什么是战略?例如,如果我们想去某个地方旅行,我们可以根据实际情况选择旅行路线。1、政策模式的定义。
如果你没有时间但不在乎钱,你可以选择坐飞机。如果你没钱,你可以选择坐公共汽车或火车。如果你稍微穷一点,你可以选择骑自行车。在编程中,我们经常会遇到类似的情况,实现某个功能的选项很多。例如,压缩文件的程序可以选择zip算法或gzip算法。
定义:策略模式定义了一系列算法,这些算法被单独打包,以便它们可以相互替换。这种模式使得算法的改变与使用计算的客户无关。
策略模式被广泛使用。在这一节中,我们将以年终奖励的计算为例进行介绍。
2.年终奖的例子。
许多公司的年终奖是根据他们的基本工资和年终表现来发放的。比如绩效S的人年终奖是4倍,绩效A的人年终奖是3倍,绩效B的人年终奖是2倍。假设财务部要求我们提供一个代码,方便他们计算员工的年终奖。
1).最初的代码实现。
我们可以编写一个名为ComputeBonus的函数来计算每个人的奖金数额。显然,为了正确工作,calculateBonus函数需要接收两个参数:员工的工资金额和他的绩效考核级别。代码如下:
var calculateBonus=函数(performanceLevel,salary){ if(performance level==' S '){ return salary * 4;} if (performanceLevel==='A' ){返回薪资* 3;} if (performanceLevel==='B' ){返回薪资* 2;}};计算奖金(' B ',20000);//输出:40000calculateBonus('S ',6000);//输出:24000可以发现这个代码很简单,但是缺点很明显。
calculateBonus函数非常庞大,包含许多if-else语句,需要覆盖所有逻辑分支。
计算奖金功能缺乏灵活性。如果增加一个新的绩效等级C,或者我们想把绩效S的奖金系数改成5,就必须深化calculateBonus函数的内部实现,这就违反了开闭原则。
这些算法的可重用性很差。如果我们需要在程序的其他部分重用这些算法来计算奖金呢?我们的选择只有复制和粘贴。因此,我们需要重构这段代码。
2)结合函数重构代码。
一般来说,最容易想到的方法是使用组合函数来重构它。我们把各种算法封装成小函数。这些小函数都有很好的名字,所以我们一眼就能知道它对应的是哪种算法,它们也可以在程序的其他部分重用。代码如下:
var performanceS=函数(工资){返回工资* 4;};var performanceA=函数(薪资){返回薪资* 3;};var performanceB=函数(薪资){返回薪资* 2;};var calculate bonus=function(performance level,薪金){ if(performance level==' S '){ return performance(薪金);} if(performance level===' A '){ return performance A(工资);} if(performance level===' B '){ return performance B(工资);}};计算奖金(' A ',10000);//输出:3万目前我们的程序有了一定程度的改进,但是这种改进非常有限,我们还没有解决最重要的问题:calculateBonus函数可能会变得越来越大,在系统发生变化时缺乏灵活性。
3)使用策略模式重构代码。
经过思考,我们想出了一个更好的方法,——,利用策略模式重构代码。模式是指定义一系列算法,并逐一封装。把不变的部分和变化的部分分开是每一个设计模式的主题,策略模式也不例外。策略模式的目的是将算法的使用与算法的实现分开。
在这个例子中,算法的使用是不变的,计算的奖金金额是根据某个算法获得的。算法的实现方式各不相同,每种性能对应不同的计算规则。
基于策略模式的程序至少由两部分组成。第一部分是一组策略类,封装了具体的算法,负责具体的计算过程。第二部分是环境类Context,它接受客户的请求,然后将请求委托给某个策略类。为此,这意味着应该在上下文中维护对策略对象的引用。
现在用策略模式重构上面的代码。第一个版本模仿传统面向对象语言中的实现。首先,我们将每个性能的计算规则封装在相应的策略类中:
var PERFoRMANCES=function(){ };performance . prototype . calculate=function(薪资){ return salary * 4;};var performance a=function(){ };performance a . prototype . calculate=函数(薪资){返回薪资* 3;};var performance b=function(){ };performance b . prototype . calculate=函数(薪资){返回薪资* 2;};接下来,定义奖金类别奖金:
var Bonus=function(){ this . salary=null;//原薪资this.strategy=null//性能级别对应的策略对象};Bonus.prototype.setSalary=函数(薪资){ this.salary=薪资;//设置员工原工资};bonus . prototype . setstrategy=function(strategy){ this . strategy=strategy;//设置员工绩效等级对应的策略对象};bonus . prototype . getbonus=function(){//获取奖金金额返回此。计算(这个。工资);//将奖金计算操作委托给对应的政策对象};在完成最终代码之前,让我们回顾一下策略模式的思想:
定义一系列的算法,一个一个的打包,让它们可以互换。
如果这句话再详细一点,就是:定义一系列算法,封装到策略类中,算法封装在策略类内部的方法中。当客户端向上下文发出请求时,上下文总是将请求委托给这些策略对象之一进行计算。
在很大程度上,“并使它们可互换”这个短语是相对于静态类型语言而言的。因为静态类型语言中有一种类型检查机制,所以每个策略类都需要实现相同的接口。只有当它们的真实类型隐藏在接口后面时,它们才能被彼此替换。但是JavaScript就没有这样的麻烦,JavaScript是一种“模糊类型”的语言,任何对象都可以替换。因此,在JavaScript中“它们可以互换使用”意味着它们有相同的目标和意图。
现在让我们完成这个例子中的剩余代码。首先,创建一个奖金对象,并为奖金对象设置一些原始数据,例如员工的原始工资。然后,某个计算奖金的政策对象也被转入奖金对象并保存。调用bonus.getBonus()计算奖金时,奖金对象本身没有计算能力,而是将请求委托给之前保存的策略对象:
var奖金=新奖金();bonus . set salary(10000);bonus . setstrategy(new PERFoRMANCES());//设置策略对象console . log(bonus . getbonus());//输出:4万奖金。设定策略(新绩效a());//设置策略对象console . log(bonus . getbonus());//输出:30000刚才我们用政策模式重构了这个计算年终奖的代码。我们可以看到,用策略模式重构后,代码变得更加清晰,各个类的职责也更加分明。然而,这段代码是基于对传统面向对象语言的模仿。在下一节中,我们将了解由JavaScript实现的策略模式。
在5.1节中,我们让策略对象从每个策略类中创建,它模拟了一些传统的面向对象语言的实现。实际上,在JavaScript语言中,函数也是对象,所以直接将策略定义为函数更简单、更直接:
var strategies={ 'S':函数(薪资){返回薪资* 4;},' A':函数(工资){返回工资* 3;},' B':函数(工资){返回工资* 2;}};同样的,Context也不一定要用Bonus类来表示,我们仍然使用calculateBonus函数作为Context来接受用户的请求。转换后,代码的结构变得更加简洁:
var strategies={ 'S':函数(薪资){返回薪资* 4;},' A':函数(工资){返回工资* 3;},' B':函数(工资){返回工资* 2;}};var calculateBonus=函数(级别,薪资){ return strategies[ level ](薪资);};console.log(calculateBonus('S ',20000));//输出:80000console.log(计算奖金(' a ',10000));//输出:300003。再解释一下例子。
一个小例子就能让我们一目了然。回想一下jquery中的动画方法。
$(div)。动画({'left: 200px'},1000,' linear ');//匀速移动$ (div)。动画({'left: 200px'},1000,' cubic ');//三次方慢动作这两个代码是在1000 ms内将div向右移动200像素,线性(匀速)和立方(三次方慢动作)是一个战略模式的套装。
我们再举一个例子。很多页面会有即时验证表。表单的每个成员都有一些不同的验证规则。
例如,在名称框中,需要验证非空的、敏感的单词和过长的字符。当然,可以通过编写三个ifelses来解决,但是这段代码的可扩展性和可维护性是可以想象的。如果表单中的元素多一点,需要检查的案例就多一点,另外写几百个If也不是不可能。
因此,最好用策略模式分别打包每个验证规则。当需要身份验证时,只需要策略的名称。就像这样:
名称输入。addvalidata ({notNull : true,脏词: true,maxLength : 30}),而notNull、maxLength等方法只需要统一返回true或false来表示是否通过验证。validataList={ notNull:函数(值){返回值!=='';},maxLength:函数(value,maxLen){ return value . length()maxLen;}}可以看出,各种验证规则可以很容易地相互修改和替换。如果有一天,产品经理建议将过长字符的限制改为60个字符。完成这项工作只需要0.5秒。
这是关于每个人的内容。
我们来谈谈题外话。2015年快过去了。是不是每个人的年终奖都很丰厚?
希望你在这一年有所收获,也可以通过这篇文章有所收获,知道什么是战略模式,理解边肖精心准备的两个例子。
版权声明:学习JavaScript设计模式(策略模式)是由宝哥软件园云端程序自动收集整理而来。如果本文侵犯了你的权益,请联系本站底部QQ或者邮箱删除。