发布于 

设计模式之策略模式

策略模式的定义是:定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换。

策略模式主要用来解决当有多种相似算法的时,使用if…else产生的难以维护的问题。它主要由三部分组成:strategy接口、具体的strategy类以及用来改变和执行策略的context类。其中,Context接受客户的请求,随后
把请求委托给某一个策略类。

使用策略模式计算奖金

很多公司的年终奖是根据员工的工资基数和年底绩效情况来发放的。例如,绩效为 S 的人年
终奖有 4 倍工资,绩效为 A 的人年终奖有 3 倍工资,而绩效为 B 的人年终奖是 2 倍工资。假设财
务部要求我们提供一段代码,来方便他们计算员工的年终奖。

不使用策略模式实现年终奖计算:

1
2
3
4
5
6
7
8
9
10
11
12
13
let 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 ); // 输出:40000
calculateBonus( 'S', 6000 ); // 输出:24000

这段代码十分简单,但存在着显而易见的缺点。if-else分支太多,calculateBonus 函数缺乏弹性,如果增加了一种新的绩效等级 C,又要重写一个判断,代码冗余。

使用策略模式重构代码:

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
interface IStrategy{
calculate(salary:number):number
}
//每种绩效和对应的策略类
class PerformanceS implements IStrategy {
calculate (salary:number) {
return salary * 4
}
}

class PerformanceA implements IStrategy {
calculate (salary:number) {
return salary * 3
}
}

class PerformanceB implements IStrategy {
calculate (salary:number) {
return salary * 2
}
}

//奖金类
class Bonus {
salary:number=0 //原始工资
strategy:any = null //绩效等级对应的策略对象

setSalary (salary:number){
this.salary = salary //设置员工的原始工资
}

setStrategy (strategy:strategyType){
this.strategy = strategy //设置员工绩效等级对应的策略对象
}

getBonus(){
//计算取得奖金数额,把计算奖金的操作委托给对应的策略对象
//类型断言,将any类型转为strategy类型
return (this.strategy as strategyType).calculate(this.salary)
}
}

let bonus = new Bonus()
bonus.setSalary(10000);
bonus.setStrategy(new PerformanceS())
console.log(bonus.getBonus())

bonus.setStrategy(new PerformanceA())
console.log(bonus.getBonus())

可以看到通过策略模式重构之后,代码变得更加清晰,各个类的职责更加鲜明。

使用策略模式实现超市选择优惠活动

在超市里购物时,通常存在两种优惠,一是满减优惠,二是某某商品打折扣。

使用策略模式实现通过其中一种优惠计算最终需要支付的金额

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
62
63
64
65
66
//strategy接口
interface strategy {
discount(money: number): number
}
//满减优惠策略
class FullandReduceStrategy implements strategy {
//满足条件的金额
private conditionMoney: number;
//减少的金额
private reduceMoney: number;

constructor(money:number, returnMoney:number){
this.conditionMoney = money;
this.reduceMoney = returnMoney
}

public discount(money: number): number {
let result: number = money;
if(money >= this.conditionMoney){
result = money - Math.floor(money / this.conditionMoney) * this.reduceMoney
}
return result
}
}
//现金折扣策略
class CashRebateStrategy implements strategy{
//折扣值
private moneyRebate:number;

constructor(moneyrabate:number){
this.moneyRebate = moneyrabate
}

discount(money: number): number {
return money * this.moneyRebate
}
}


class Context {
private strategy:any = null
private money:number

constructor(money:number){
this.money = money
}

//设置优惠策略
setStrategy(strategy:strategy){
this.strategy = strategy
}

//执行策略
execute(){
return (this.strategy as strategy).discount(this.money)
}
}

//实现满减优惠
const context:Context = new Context(50)
context.setStrategy(new FullandReduceStrategy(50,2))
console.log(context.execute())

//实现折扣优惠
context.setStrategy(new CashRebateStrategy(0.5))
console.log(context.execute())

通过使用策略模式,我们消除了原程序中大片的条件分支语句。所有跟计算有关的逻辑不再放在 Context 中,而是分布在各个策略对象中。Context 并没有计算的能力,而是把这个职责委托给了某个策略对象。每个策略对象负责的算法已被各自封装在对象内部。当我们对这些策略对象发出“计算”的请求时,它们会返回各自不同的计算结果,这也是对象多态性的体现。