时势造英雄——策略模式
策略模式的定义
策略模式定义了一系列的算法,并将每一个算法封装起来,而且使他们还可以相互替换。策略模式让算法独立于使用它的客户而独立变化。
策略模式的使用场景
- 针对同一类型问题的多种处理方式,仅仅是具体行为有差别时。
- 需要安全地封装多种同一类型的操作时。
- 出现同一抽象类有多个子类,而又需要使用if-else或者switch-case来选择具体子类时。
举个例子:当有一个数组,需要排序时,我们有很多排序的算法,插入排序、归并排序、冒泡排序等,一种常规的方法是将多种算法写在同一个类中,每个方法对应具体的排序算法,也可以将这些排序算法封装在一个统一的方法中,通过if…else…或者case等条件判断来选择具体的算法。这两种方式称为硬编码。然而,多个算法集中在一个类中时,这个类就会变得臃肿,维护成本变高。如果需要增加一种新的排序算法,需要修改封装算法的源代码,违反了OCP原则和单一职责原则。这时候就可以使用策略模式来解决这个问题。
策略模式的实例
现在我们来提供一个计算出行方式的费用功能。通常,我们会写成这样:
/**
* description: 价格计算器
*/
public class PriceCalculator {
//公交车类型
private static final int BUS = 1;
//地铁类型
private static final int SUBWAY = 2;
/**
* description: 公交车价格计算,十公里之内一块钱,超过十公里之后每加一元钱可以乘5公里
*/
private int busPrice(int km) {
//超过十公里的总距离
int extraTotal = km - 10;
//超过的距离是五公里的倍数
int extraFactor = extraTotal / 5;
//超过的距离对五公里的倍数
int fraction = extraTotal % 5;
//价格计算
int price = 1 + extraFactor;
return fraction > 0 ? ++price : price;
}
/**
* description: 地铁价格计算,6公里内3元,6-12公里4元,12-22公里5元,22-32公里6元,其他的都按7元算
*/
private int subwayPrice(int km) {
if (km <= 6) {
return 3;
} else if (km < 12) {
return 4;
}else if (km > 12 && km < 22) {
return 5;
}else if (km > 22 && km < 32) {
return 6;
}
return 7;
}
/**
* description: 供外界调用,通过type选择计算方式
*/
int calculatePrice(int km, int type) {
if (type == BUS) {
return busPrice(km);
} else if (type == SUBWAY) {
return subwayPrice(km);
}
return 0;
}
}
如果需要再向这个类中添加一个出租车的价格计算,就需要在这个类中修改代码,有可能一段代码是几个出行方式所共用的,这就容易引入错误。而且还需要在if…else…那增加出行方式,使得缠绕其中。若是再多加几种出行方式,维护起来将会很难,接下来将使用策略模式进行重构。
定义一个计算价格的抽象接口
CalculateStrategy.class
/**
* description: 计算接口
*/
public interface CalculateStrategy {
/**
* description: 按距离来计算价格
*/
int calculatePrice(int km);
}
每种出行方式我们都有一个独立的策略类,这些策略类实现了CalculateStrategy接口。
BusStrategy.class
/**
* description: 公交车价格计算策略
*/
public class BusStrategy implements CalculateStrategy {
/**
* description: 公交车价格计算,十公里之内一块钱,超过十公里之后每加一元钱可以乘5公里
*/
@Override
public int calculatePrice(int km) {
//超过十公里的总距离
int extraTotal = km - 10;
//超过的距离是五公里的倍数
int extraFactor = extraTotal / 5;
//超过的距离对五公里的倍数
int fraction = extraTotal % 5;
//价格计算
int price = 1 + extraFactor;
return fraction > 0 ? ++price : price;
}
}
SubwayStrategy.class
/**
* description: 地铁价格计算策略
*/
public class SubwayStrategy implements CalculateStrategy {
/**
* description: 地铁价格计算,6公里内3元,6-12公里4元,12-22公里5元,22-32公里6元,其他的都按7元算
*/
@Override
public int calculatePrice(int km) {
if (km <= 6) {
return 3;
} else if (km < 12) {
return 4;
}else if (km > 12 && km < 22) {
return 5;
}else if (km > 22 && km < 32) {
return 6;
}
return 7;
}
}
最后,向外提供调用的api
TranficCalculator.class
/**
* description: 出行价格计算器
*/
public class TranficCalculator {
CalculateStrategy calculateStrategy;
/**
* description: 提供给用户使用,设置具体的策略
*/
public void setCalculateStrategy(CalculateStrategy calculateStrategy) {
this.calculateStrategy = calculateStrategy;
}
/**
* description: 设置策略后,调用计算价格
*/
public int calculatePrice(int km) {
return calculateStrategy.calculatePrice(km);
}
}
小结
优点
- 结构清晰明了,使用简单直观。
- 耦合度相对而言较低,扩展方便。
- 操作封装也更为彻底,数据更为安全。
缺点
随着策略的增加,子类也会变得繁多。