介绍
策略模式定义了一系列算法,并将每一个算法封装起来,而且使它们还可以相互替换。策略模式让算法独立于使用它的客户而独立变化
使用场景
- 针对同一类型的多种处理方式,仅仅是具体行为有差别时。
- 需要安全地封装多种同一类型的操作时。
- 出现同一抽象类有多个子类,而又需要使用 if-else 或者 switch-case 来选择具体子类时。
角色介绍
- Context——用来操作策略的上下文环境;
- Strategy——策略的抽象;
- ConcreteStrategyA,ConcreteStrategyB——具体的策略实现。
简单实现
需求:下面以坐公交工具的费用计算来演示。
第一个版本的代码:
public class PriceCalculator {
//公交车类型
private static final int BUS = 1;
//地铁类型
private static final int SUBWAY = 2;
/**
* 公交车,十公里之内一元钱,超过十公里之后每加一元钱可以乘5公里
* @param km 公里
* @return
*/
private int busPrice(int km) {
//超过十公里的总距离
int extraTotal = km - 10;
//超过的距离是5公里的倍数
int extraFactor = extraTotal / 5;
//超过的距离对5公里取余
int fraction = extraTotal % 5;
//价格计算
int price = 1 + extraFactor * 1;
return fraction > 0 ? ++price : price;
}
/**
* 6公里(含)内3元;6~12公里(含)4元;12~22公里(含)5元;22~32公里(含)6元;
* @param km 公里
* @return
*/
private int subwayPrice(int km) {
if (km <= 6) {
return 3;
} else if (km <= 12) {
return 4;
} else if (km <= 22) {
return 5;
} else if (km <= 32) {
return 6;
}
//其他距离简化为7元
return 7;
}
private int calculatePrice(int km, int type) {
if (type == BUS) {
return busPrice(km);
} else if (type == SUBWAY) {
return subwayPrice(km);
}
return 0;
}
public static void main(String[] args) {
PriceCalculator calculator = new PriceCalculator();
System.out.println("坐16公里的公交车票价为:" + calculator.calculatePrice(16, BUS));
System.out.println("坐16公里的地铁票价为:" + calculator.calculatePrice(16, SUBWAY));
}
}
当增加一种出行方式时,如出租车,那么就需要在 PriceCalculator 中增加一个方法来计算出租车出行的价格,并且在 calculatePrice 函数中增加一个判断,新增代码大致如下:
public class PriceCalculator {
//公交车类型
private static final int BUS = 1;
//地铁类型
private static final int SUBWAY = 2;
//出租车类型
private static final int TAXI = 3;
private int taxiPrice(int km) {
return km * 2;
}
private int calculatePrice(int km, int type) {
if (type == BUS) {
return busPrice(km);
} else if (type == SUBWAY) {
return subwayPrice(km);
} else if (type == TAXI) {
return taxiPrice(km);
}
return 0;
}
}
当修改计算方式或者增加出行方式时,有需要增加 if-else 代码,这样会使得代码变得难以维护。
下面为策略模式实现代码:
-
计算接口
public interface CalculateStrategy { /** * 按距离来计算价格 * @param km 公里 * @return 返回价格 */ int calculatePrice(int km); }
-
公交车价格计算策略
public class BusStrategy implements CalculateStrategy { /** * 公交车,十公里之内一元钱,超过十公里之后每加一元钱可以乘5公里 * @param km 公里 * @return */ @Override public int calculatePrice(int km) { //超过十公里的总距离 int extraTotal = km - 10; //超过的距离是5公里的倍数 int extraFactor = extraTotal / 5; //超过的距离对5公里取余 int fraction = extraTotal % 5; //价格计算 int price = 1 + extraFactor * 1; return fraction > 0 ? ++price : price; } }
-
地铁价格计算策略
public class SubwayStrategy implements CalculateStrategy { /** * 6公里(含)内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 <= 12) { return 4; } else if (km <= 22) { return 5; } else if (km <= 32) { return 6; } //其他距离简化为7元 return 7; } }
-
出行计算器
public class TranficCalculator { private CalculateStrategy mStrategy; public void setStrategy(CalculateStrategy strategy) { mStrategy = strategy; } public int calculatePrice(int km) { return mStrategy.calculatePrice(km); } public static void main(String[] args) { TranficCalculator calculator = new TranficCalculator(); calculator.setStrategy(new BusStrategy()); System.out.println("公交车乘坐16公里的价格:" + calculator.calculatePrice(16)); calculator.setStrategy(new SubwayStrategy()); System.out.println("地铁乘坐16公里的价格:" + calculator.calculatePrice(16)); }
如果需要增加一个出租车计算策略类,代码如下:
-
出租车计算策略
public class TaxiStrategy implements CalculateStrategy { @Override public int calculatePrice(int km) { return km * 2; } }
-
出行价格计算器
public class TranficCalculator { private CalculateStrategy mStrategy; public void setStrategy(CalculateStrategy strategy) { mStrategy = strategy; } public int calculatePrice(int km) { return mStrategy.calculatePrice(km); } public static void main(String[] args) { TranficCalculator calculator = new TranficCalculator(); calculator.setStrategy(new TaxiStrategy()); System.out.println("出租车乘坐16公里的价格:" + calculator.calculatePrice(16)); } }
从上面可以看出,使用 if-else 来解决问题,优点是实现简单,层级单一;但缺点也很明显,即代码臃肿,逻辑复杂,难以升级与维护,没有结构可言。
后者是通过建立抽象,将不同的策略构建成一个具体的策略实现,通过不同的策略实现算法替换。在简化逻辑、结构的同时,增强了系统的可读性、稳定性、可扩展性。
Android源码中的实现
TimeInterpolator 时间插值器,用在属性动画中。
总结
- 优点
- 结构清晰明了,使用简单直观;
- 耦合度相对而言较低,扩展方便;
- 操作封装也更彻底,数据更为安全。
- 缺点
- 随着策略的增加,子类也会变得繁多。