策略模式-面向接口编程
定义
定义了一系列算法,并将每一个算法封装起来,而且使他们可以相互替换。让算法独立于使用它的客户而独立变化。
使用场景
- 同一类型的问题又多种算法解决。
- 需要安全的封装多种同一类型的操作。
- 出现同一抽象类有多个子类,而又需要if-else或者switch-case来选择子对象。
UML类图
- Context – 操作Stragety上下文,面向接口的类
- Stragety – 策略的抽象
- ConcreteStragetyA/B – 具体的策略实现
策略模式的简单例子
例如,北京提高公交价格,不再是单一票价制,而是分段计价,也就是乘坐的距离越远,价格越高。显然,公交车和地铁的价格计算方式是不一样的,但是我们的示例中需要计算成不同交通工具的成本。下面是我们第一个版本的代码:
/**
* Created by Joki on 2017/4/4.
*/
public class PriceCalculator {
//公交车类型
private static final int BUS = 1;
//地铁类型
private static final int SUBWAY = 2;
public static void main(String[] args){
PriceCalculator priceCalculator = new PriceCalculator();
System.out.println("坐16公里的公交车票价为:" + priceCalculator.caculatePrice(16,BUS));
System.out.println("坐16公里的地铁票价为:" + priceCalculator.caculatePrice(16,SUBWAY));
}
/**
* 北京公交车,十公里之内一元钱,超过十公里之后每加一元钱可以乘坐五公里
* @param km
* @return
*/
private int busPrice(int km){
//超过十公里的总距离
int extraTotal = km - 10;
//超过的距离是五的倍数
int extraFactor = extraTotal/5;
//超过的距离对五公里取余
int fraction = extraFactor % 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 > 6 && km <= 12){
return 4;
}else if (km > 12 && km <= 22){
return 5;
}else if (km > 22 && km <=32){
return 6;
}
return 7;
}
int caculatePrice(int km, int type) {
if (type == BUS){
return busPrice(km);
}else if(type == SUBWAY){
return subwayPrice(km);
}
return 0;
}
}
PriceCalculatior类很明显不是单一职责,首先它承担了计算公交车和地铁乘坐价格的职责,另一个问题是通过if-else语句来判断使用哪种计算形式。当我们增加另外的出行方式时,就必须在PriceCalculator中增加一个方法来计算出租车出行的价格并且在caculatePrice(int km, int type)方法中增加一个判断,代码将会变得比较混乱,各种if-else语句缠绕其中。
下面我们用策略模式进行重构:
首先定义一个抽象的价格计算接口:
/**
*计算接口
*/
public interface CalculateStrategy {
int calculatePrice(int km);
}
对于每一种出行方式都有一个独立的计算策略类:
/**
* 公交车价格计算策略
*/
public class BusStrategy implements CalculateStrategy {
/**
* 北京公交车,十公里之内一元钱,超过十公里之后每加一元钱可以乘坐五公里
* @param km
* @return
*/
@Override
public int calculatePrice(int km) {
//超过十公里的总距离
int extraTotal = km - 10;
//超过的距离是五的倍数
int extraFactor = extraTotal/5;
//超过的距离对五公里取余
int fraction = extraFactor % 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 > 6 && km <= 12){
return 4;
}else if (km > 12 && km <= 22){
return 5;
}else if (km > 22 && km <=32){
return 6;
}
return 7;
}
}
还有一个扮演Context角色的类:
/**
* 公交出行价格计算器
*/
public class TranficCalculator {
public static void main(String[] args){
TranficCalculator calculator = new TranficCalculator();
//设置计算策略
calculator.setmStrategy(new BusStrategy());
System.out.println("坐16公里的公交车票价为:" + calculator.calculatePrice(16));
}
CalculateStrategy mStrategy;
public void setmStrategy(CalculateStrategy mStrategy) {
this.mStrategy = mStrategy;
}
public int calculatePrice(int km){
return mStrategy.calculatePrice(km);
}
}
这种方案在隐藏实现的同时,可扩展性变得很强。例如,当我们需要增加出租车计算器时,只需要添加一个出租车计算策略类,然后将该策略设置给TranficCalculator即可。
/**
* 出租车计算策略
*/
public class TaxiStrategy implements CalculateStrategy{
/**
* 简单设置为公里数的2倍
* @param km
* @return
*/
@Override
public int calculatePrice(int km) {
return km * 2;
}
}
/**
* 出租出行价格计算器
*/
public class TranficCalculator {
public static void main(String[] args){
TranficCalculator calculator = new TranficCalculator();
//设置计算策略
calculator.setmStrategy(new TaxiStrategy());
System.out.println("坐16公里的出租车票价为:" + calculator.calculatePrice(16));
}
CalculateStrategy mStrategy;
public void setmStrategy(CalculateStrategy mStrategy) {
this.mStrategy = mStrategy;
}
public int calculatePrice(int km){
return mStrategy.calculatePrice(km);
}
}
优点
- 结构清晰明了,使用简单直观
- 耦合度相对较低,扩展方便
- 操作封装更为彻底,数据更为安全
缺点
- 随着策略的增加,子类会变得繁多