策略模式

介绍

实现某一个功能的时候可能会有多种的算法或者策略,我们根据实际的情况来选择不同的算法或者策略来完成该功能,例如,排序吧算法,可以使用插入排序,归并排序,冒泡排序等。

针对这种情况,一种常规的写法是将多个算法写在同一个类中。例如需要多种排序算法,可以将这些排序算法写到一个类中,每一个方法对应一个具体的算法,当然我们也可以将这些排序算法封装在一个统一的方法中,通过if-else或者case等条件判断语句来选择具体的算法。这两种实现方式我们都可以称之为硬编码。然而,当很多个算法集中在一个类中的时候,这个类就会变得很臃肿,这个类的维护的成本就会变得很高,在维护的时候,很容易引发错误,如果我们需要增加一种新的排序算法,需要修改封装算法的源代码。这明显违背了开闭原则。

如果将这些算法或者策略模式抽象出来,提供一个统一的接口,不同的算法或者策略会有不同的实现,这样在程序客户端就可以通过注入不同的实现对象来实现算法或者策略的动态替换,这种模式的可扩展性,可维护性也就更高,这就是策略模式。

使用场景

  • 针对同一类型问题的多种处理方式,仅仅是具体的行为有差别的时候
  • 需要安全的封装多种同一类型的操作时
  • 出现同一抽象类有多个子类,而又需要if-else或者switch-case来选择具体的子类的时候

简单实现

公交车跟地铁的分段计费

不推荐的写法

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
public class PriceCalculate {

private static final int BUS=1;

private static final int SUBWAY=2;

public static void main(String[] args){

PriceCalculate priceCalculate = new PriceCalculate();

System.out.println("公交车乘坐15公里的价格"+priceCalculate.calculate(15,BUS));
System.out.println("地铁乘坐15公里的价格"+priceCalculate.calculate(15,SUBWAY));
}



private int busPrice(int km){

int extraTotal = km - 10;

int extraFactor = extraTotal/5;

int fraction = extraTotal%5;

int price = 1+extraFactor*1;

return fraction>0?++price:price;

}


private int subwayPrice(int km){

if(km<=6){
return 3;
}else if(km>6&&km<12){
return 4;
}else if(km>12&&km<22){
return 5;
}else if(km>22&&km<32){
return 6;
}

return 7;

}


int calculate(int km,int type){

if(type==BUS){
return busPrice(km) ;
}else if(type==SUBWAY){
return subwayPrice(km);
}
return 0;
}
}

很明显的问题,上述代码并不是单一职责的,首先他承担了计算公交车gne乘坐价格的职责,另一个问题是通过if-else的形式来判断使用哪一种计算形式。当我们增加一种出行方式的时候,如出租车,那么我们就需要在PriceCalculator中增加一个方法来计算出租车的价格,并且在calculatePrice(int km,int type)函数中增加一个判断。代码很混乱,各种if-else语句缠绕其中。当价格的计算方法变化的时候,需要直接修改这个类的代码,那么很有可能有一段代码是其他几个计算方法所共同使用的,这就容易引入错误。另外在增加出行的方式的时候,我们又需要在calculatePrice中添加if-else此时很有可能是复制上一次的if-else然后手动的修改,手动复制代码也是容易引进错误的做法之一。这类代码必然是难以应对变化的,他会使得代码变得臃肿难以维护,解决这类问题的办法就是策略模式。

策略模式的写法

1
2
3
4
5
6
public interface CalculateStrategy {

//按距离计算价格;
int calculatePrice(int km);

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class BusStrategy implements CalculateStrategy {

/*
* 北京公交车十公里以内一元钱,超过十公里后,没增加一元钱可以乘5公里
* @param km 千米
* @return
* */
@Override
public int calculatePrice(int km) {

int extraTotal = km - 10;

int extraFactor = extraTotal/5;

int fraction = extraTotal%5;

int price = 1+extraFactor*1;

return fraction>0?++price:price;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class SubwayStrategy implements CalculateStrategy {


/*
* 六公里内3元,6-12公里4元,12-22公里5元,22-32公里6元。
* @param km 公里
* @return
* */
@Override
public int calculatePrice(int km) {

if(km<=6){
return 3;
}else if(km>6&&km<12){
return 4;
}else if(km>12&&km<22){
return 5;
}else if(km>22&&km<32){
return 6;
}

return 7;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class TranficCalculate {

public static void main(String[] args){

TranficCalculate tranficCalculate = new TranficCalculate();
tranficCalculate.setmStrategy(new BusStrategy());
System.out.println("公交车乘坐15公里的价格"+tranficCalculate.calculate(15));

}

CalculateStrategy mStrategy;

public void setmStrategy(CalculateStrategy calculateStrategy){
this.mStrategy=calculateStrategy;
}

public int calculate(int km){
return mStrategy.calculatePrice(km);
}

}

去除了各种if-else语句,结构也变得很清晰。

这种方案在隐藏实现的同时,可扩展性也变得很强,例如当我们需要增减出组车的计算策略的时候,只需要增加一个出租车策略计算类,然后将该策略传递给TranficCalculator,最好直接通过TranficCalculator对象的计算方法即可。

1
2
3
4
5
6
public class TaxiStrategy implements CalculateStrategy {
@Override
public int calculatePrice(int km) {
return km*2;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class TranficCalculate {

public static void main(String[] args){

TranficCalculate tranficCalculate = new TranficCalculate();
tranficCalculate.setmStrategy(new TaxiStrategy());
System.out.println("出租车乘坐15公里的价格"+tranficCalculate.calculate(15));

}

CalculateStrategy mStrategy;

public void setmStrategy(CalculateStrategy calculateStrategy){
this.mStrategy=calculateStrategy;
}

public int calculate(int km){
return mStrategy.calculatePrice(km);
}

}

通过上述的示例我们可以清除的看出两者的区别,使用if-else来解决问题,虽然实现简单,类型层级单一,但暴露出的问题非常明显,即代码臃肿,逻辑复杂,难以升级跟维护,没有结构可言,后者则是通过建立抽象,讲不同的策略构建成一个具体的策略实现,通过不同的策略实现算法替换。在简化逻辑跟结构的同时,增强了系统的可读性,稳定性,可扩展性,这对于较为复杂的业务逻辑显得更为直观,扩展也更为方便。

当前网速较慢或者你使用的浏览器不支持博客特定功能,请尝试刷新或换用Chrome、Firefox等现代浏览器