JS设计模式——策略模式

定义一系列算法,把它们一个个封装起来,并且使它们可以相互替换。实际中“算法”的含义可以扩散开来:算法也可以指代不同的“业务规则”。

一、定义

定义一系列算法,把它们一个个封装起来,并且使它们可以相互替换。实际中“算法”的含义可以扩散开来:算法也可以指代不同的“业务规则”。

一个典型的策略模式主要包含两个部分:

  1. 一组策略类,负责封装具体的算法。
  2. 一个环境类Context,Context负责接受客户的请求,并将请求委托给某一个策略类。

策略模式的优点:

  • 策略模式可以解耦具体的策略和对外的接口,使得策略维护与切换更容易。
  • 利用组合、委托和多态等技术,可以有效的避免多重条件选择语句。
  • 策略模式符合开放-封闭原则,封装的算法易于切换和扩展。
  • 整体方案的可移植性更好,可提高代码复用率。
  • Context拥有执行算法的能力,这也是继承的一种轻便的替代方案(子类可以继承父类的各种策略,相当于把算法封装在父类中,JavaScript中的策略模式可以直接把算法封装在一个个函数或者对象中,所以说是一种轻便的继承替代方案)。

二、JavaScript中的实现

问题背景:

公司根据员工绩效发奖金bonus,评级为S、A和B,不同级别对应不同的乘数。

实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var strategies={  //strategies对象存储一组策略
"S":function(salary){
return salary*4;
},
"A":function(salary){
return salary*3;
},
"B":function(salary){
return salary*2;
}
};

var calcBonus=function(level,salary){ //分配策略
return strategies[level](salary);
};

calcBonus('S',2000); //8000

三、使用策略模式实现缓动动画

问题背景:

写一个动画类和一些缓动算法,让目标以各种各样的缓动效果在页面中运动。(本例摘自《JavaScript设计模式与开发实践

实现

(codepen):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
var tween={  //缓动函数
liner: function(t,b,c,d){
return c*t/d + b;
},
easeIn: function(t,b,c,d){
return c*(t/=d)*t + b;
},
easeOut: function(t,b,c,d){
return -c *(t/=d)*(t-2) + b;
},
sineaseIn: function(t,b,c,d){
return c*(t/=d)*t*t*t*t + b;
}
};

var Animate=function(dom){
this.dom=dom; //动画节点
this.startTime=0; //开始时间
this.startPos=0; //初始位置
this.endPos=0; //结束位置
this.propertyName=null; //动画属性
this.easing=null; //缓动算法
this.duration=null; //持续时间
};

Animate.prototype.start=function(propertyName,endPos,duration,easing){
//初始化参数
this.startTime=+new Date();
this.startPos=this.dom.getBoundingClientRect()[propertyName];
this.propertyName=propertyName;
this.endPos=endPos;
this.duration=duration;
this.easing=tween[easing];

var self=this;
//启动定时器,并开始执行动画
var timeId=setInterval(function(){
if(self.step()===false){
clearInterval(timeId); //动画结束
}
},19);
};

Animate.prototype.step=function(){
var t=+new Date(); //当前时间
if(t>=this.startTime+this.duration){
this.update(this.endPos); //动画结束
return false;
}

var pos=this.easing(t-this.startTime,this.startPos,this.endPos-this.startPos,this.duration);
this.update(pos); //更新小球位置
};

Animate.prototype.update=function(pos){
this.dom.style[this.propertyName]=pos+"px";
};

var div=document.getElementById("target");
var animate=new Animate(div);
animate.start('left',200,1000,'sineaseIn');

getBoundingClientRect()