视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
设计模式的策略模式怎样在前端中使用
2020-11-27 19:47:21 责编:小采
文档


上边这句话,从字面来看很简单。但是如何在开发过程中去应用,仅凭一个定义依然是一头雾水。以笔者曾经做过的商户进销存系统为例:

某超市准备举行促销活动,市场人员经过调查分析制定了一些促销策略:
  1. 购物满100减10

  2. 购物满200减30

  3. 购物满300减50

  4. 。。

收银软件的界面是这样的(简单示意):

我们应该如何计算实际消费金额?

最初的实现是这样的:

//方便起见,我们把各个促销策略定义为枚举值:0,1,2...
var getActualTotal = function(onSaleType,originTotal){
 if(onSaleType===0){
 return originTotal-Math.floor(originTotal/100)*10
 }
 if(onSaleType===1){
 return originTotal-Math.floor(originTotal/200)*30
 }
 if(onSaleType===0){
 return originTotal-Math.floor(originTotal/300)*50
 }
}
getActualTotal(1,2680); //2208

上面这段代码很简单,而且缺点也很明显。随着我的满减策略逐渐增多,getActualTotal函数会越变越大,而且充满了if判断,稍一疏忽就容易弄错。

OK,有人说我很懒,虽然这样不够优雅但并不影响我的使用,毕竟满减策略再多也多不到哪去。
我只能说,需求永远不是程序员定的。这时,市场人员说我们新版程序添加了会员功能,我们需要支持以下的促销策略:

会员促销策略:
  1. 会员充300返60,且首单打9折

  2. 会员充500返100,且首单打8折

  3. 会员充1000返300,且首单打7折

  4. ...

这个时候,如果你还在原先的getActualTotal函数中继续添加if判断,我想如果你的领导review你这段代码,可能会怀疑自己当初怎么把你招进来。

OK,我们终于下定决心要重构促销策略的代码,我们可以这么做:

var vipPolicy_0=function(originTotal){
 return originTotal-Math.floor(originTotal/100)*10
}
var vipPolicy_1=function(originTotal){
 return originTotal-Math.floor(originTotal/200)*30
}
...
//会员充1000返300
var vipPolicy_10=function(account,originTotal){
 if(account===0){
 account+=1300;
 return originTotal*0.9
 }else{
 account+=1300;
 return originTotal;
 }
 return originTotal-Math.floor(originTotal/200)*30
}
...
var vipPolicy_n=function(){
 ...
}
var getActualTotal=function(onSaleType,originTotal,account){
 switch(onSaleType){
 case 0:
 return vipPolicy_0(originTotal);
 case 1:
 return vipPolicy_0(originTotal);
 ...
 case n:
 return ...
 default:
 return originTotal;
 }
}

好了,现在我们每种策略都有自己的空间了,看起来井井有条。但是还有两个问题没有解决:

  1. 随着促销策略的增加,getActualTotal的代码量依然会越来越大

  2. 系统缺乏弹性,如果需要增加一种策略,那么除了添加一个策略函数,还需要修改switch...case..语句

让我们再来回顾一下策略模式的定义:

定义一系列的算法,把它们一个个封装起来,并且使它们可互相替换

在我们的例子中,每种促销策略的实现方式是不一样的,但我们最终的目的都是为了求得实际金额。策略模式可以把我们对促销策略的算法一个个封装起来,并且使它们可互相替换而不影响我们对实际金额的求值,这正好是我们所需要的。

下面我们用策略模式来重构上面的代码:

var policies={
 "Type_0":function(originTotal){
 return originTotal-Math.floor(originTotal/100)*10 
 },
 "Type_1":function(originTotal){
 return originTotal-Math.floor(originTotal/200)*30 
 },
 ...
 "Type_n":function(originTotal){
 ... 
 }
}
var getActualTotal=function(onSaleType,originTotal,account){
 return policies["Type_"+onSaleType](originTotal,account)
}
//执行
getActualTotal(0,2680.00);//2208

分析上面的代码我们发现,不管促销策略如何增加,getActualTotal函数完全不需要再变化了。我们要做的,就是增加新策略的函数而已。

通过策略模式的代码,我们消除了让人反胃的大片条件分支语句,getActualTotal本身并没有计算能力,而是将计算全权委托给了策略函数。

由此我们可以总结出策略模式实现的要点:

  1. 将变化的算法封装成的策略函数,并负责具体的计算

  2. 委托函数,该函数接受客户请求,并将请求委托给某一个具体的策略函数

用一张UML图表示如下:

怎么样?现在看到上面这张图是不是有了了然于胸的感觉?那就赶紧去试一试策略模式吧!


参考书籍:

  1. 《设计模式:可复用面向对象软件的基础》

  2. 《大话设计模式》

  3. 《Javascript设计模式与开发实践》

做前端开发已经好几年了,对设计模式一直没有深入学习总结过。随着架构相关的工作越来越多,越来越能感觉到设计模式成为了我前进道路上的一个阻碍。所以从今天开始深入学习和总结经典的设计模式以及面向对象的几大原则。

今天第一天,首先来讲策略模式。

什么是策略模式?

GoF四兄弟的经典《设计模式》中,对策略模式的定义如下:

定义一系列的算法,把它们一个个封装起来,并且使它们可互相替换。

上边这句话,从字面来看很简单。但是如何在开发过程中去应用,仅凭一个定义依然是一头雾水。以笔者曾经做过的商户进销存系统为例:

某超市准备举行促销活动,市场人员经过调查分析制定了一些促销策略:
  1. 购物满100减10

  2. 购物满200减30

  3. 购物满300减50

  4. 。。

收银软件的界面是这样的(简单示意):

我们应该如何计算实际消费金额?

最初的实现是这样的:

//方便起见,我们把各个促销策略定义为枚举值:0,1,2...
var getActualTotal = function(onSaleType,originTotal){
 if(onSaleType===0){
 return originTotal-Math.floor(originTotal/100)*10
 }
 if(onSaleType===1){
 return originTotal-Math.floor(originTotal/200)*30
 }
 if(onSaleType===0){
 return originTotal-Math.floor(originTotal/300)*50
 }
}
getActualTotal(1,2680); //2208

上面这段代码很简单,而且缺点也很明显。随着我的满减策略逐渐增多,getActualTotal函数会越变越大,而且充满了if判断,稍一疏忽就容易弄错。

OK,有人说我很懒,虽然这样不够优雅但并不影响我的使用,毕竟满减策略再多也多不到哪去。
我只能说,需求永远不是程序员定的。这时,市场人员说我们新版程序添加了会员功能,我们需要支持以下的促销策略:

会员促销策略:
  1. 会员充300返60,且首单打9折

  2. 会员充500返100,且首单打8折

  3. 会员充1000返300,且首单打7折

  4. ...

这个时候,如果你还在原先的getActualTotal函数中继续添加if判断,我想如果你的领导review你这段代码,可能会怀疑自己当初怎么把你招进来。

OK,我们终于下定决心要重构促销策略的代码,我们可以这么做:

var vipPolicy_0=function(originTotal){
 return originTotal-Math.floor(originTotal/100)*10
}
var vipPolicy_1=function(originTotal){
 return originTotal-Math.floor(originTotal/200)*30
}
...
//会员充1000返300
var vipPolicy_10=function(account,originTotal){
 if(account===0){
 account+=1300;
 return originTotal*0.9
 }else{
 account+=1300;
 return originTotal;
 }
 return originTotal-Math.floor(originTotal/200)*30
}
...
var vipPolicy_n=function(){
 ...
}
var getActualTotal=function(onSaleType,originTotal,account){
 switch(onSaleType){
 case 0:
 return vipPolicy_0(originTotal);
 case 1:
 return vipPolicy_0(originTotal);
 ...
 case n:
 return ...
 default:
 return originTotal;
 }
}

好了,现在我们每种策略都有自己的空间了,看起来井井有条。但是还有两个问题没有解决:

  1. 随着促销策略的增加,getActualTotal的代码量依然会越来越大

  2. 系统缺乏弹性,如果需要增加一种策略,那么除了添加一个策略函数,还需要修改switch...case..语句

让我们再来回顾一下策略模式的定义:

定义一系列的算法,把它们一个个封装起来,并且使它们可互相替换

在我们的例子中,每种促销策略的实现方式是不一样的,但我们最终的目的都是为了求得实际金额。策略模式可以把我们对促销策略的算法一个个封装起来,并且使它们可互相替换而不影响我们对实际金额的求值,这正好是我们所需要的。

下面我们用策略模式来重构上面的代码:

var policies={
 "Type_0":function(originTotal){
 return originTotal-Math.floor(originTotal/100)*10 
 },
 "Type_1":function(originTotal){
 return originTotal-Math.floor(originTotal/200)*30 
 },
 ...
 "Type_n":function(originTotal){
 ... 
 }
}
var getActualTotal=function(onSaleType,originTotal,account){
 return policies["Type_"+onSaleType](originTotal,account)
}
//执行
getActualTotal(0,2680.00);//2208

分析上面的代码我们发现,不管促销策略如何增加,getActualTotal函数完全不需要再变化了。我们要做的,就是增加新策略的函数而已。

通过策略模式的代码,我们消除了让人反胃的大片条件分支语句,getActualTotal本身并没有计算能力,而是将计算全权委托给了策略函数。

由此我们可以总结出策略模式实现的要点:

  1. 将变化的算法封装成的策略函数,并负责具体的计算

  2. 委托函数,该函数接受客户请求,并将请求委托给某一个具体的策略函数

用一张UML图表示如下:

怎么样?现在看到上面这张图是不是有了了然于胸的感觉?那就赶紧去试一试策略模式吧!

相信看了本文案例你已经掌握了方法,更多精彩请关注Gxl网其它相关文章!

推荐阅读:

怎样使用JS+H5实现微信摇一摇

如何对微信小程序进行开发

下载本文
显示全文
专题