策略模式的定义:
策略模式定义了一系列的算法,并将每一个算法封装起来,而且使他们还可以相互替换,策略模式让算法独立于使用它的客户端而独立变化。
策略模式的使用场景:
- 针对同一类型问题的多种处理方式,仅仅是具体行为有差别时
- 需要安全地封装多种同一类型的操作时
- 出现同一抽象类有多个子类,而又需要使用if-else或者 switch-case 来选择具体子类时
类图:
- Context:用来操作策略的上下文环境
- Stragety:策略的抽象
- ConcreteStragegyA,ConcreteStragegyB,ConcreteStragegyC:具体的策略实现
简单实现:
通常如果一个问题有多个解决方案时,比较简单的方式就是利用if-else或者switch-case 方式来根据不同的场景选择不同的解决方案,OK,咱们以北京的公交车为例,参考《Android源码设计模式》,首先看下通常的编写方式
public class PriceCalculator {
//公交车类型
private static final int BUS = 1;
//地铁类型
private static final int SUBWAY = 2;
public static void main(String[] args) {
PriceCalculator calculator = new PriceCalculator();
System.out.println("坐16公里的公交车票价位:" + calculator.calculatePrice(16, BUS));
}
/**
* 北京公交车,十公里之内一元钱,超过十公里之后每加一元钱可以乘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;
}
return 7;
}
private int calculatePrice(int km, int type) {
if (type == BUS) {
return busPrice(km);
} else if (type == SUBWAY) {
return subwayPrice(km);
}
return 0;
}
}
如果想增加一个选择,只能在else-if中添加
private int calculatePrice(int km, int type) {
if (type == BUS) {
return busPrice(km);
} else if (type == SUBWAY) {
return subwayPrice(km);
}else if(type == TAXT) {
return taxiPrice(km);
}
return 0;
}
很明显,PriceCalculator 类不是单一职责,1.计算公交车和地铁乘坐价格的职责。2.还有就是通过if-else的形式来判断使用哪种计算形式。当增加一种出行方式时,就需要增加一个方法。如上代码所示。整体代码比较混乱,这就是我们的主人公—策略模式登场的时候了。我们可以把每一种情景独立成一个函数,然后外部调用方法即可,但是这也是另一种耦合形式,对于可变性较大的算法族来说还是不太适合。
策略模式代码重构如下:
//计算接口
public interface CalculateStrategy {
/**
* 按距离来计算价格
* @param km
* @return
*/
int calculatePrice(int km);
}
//公交车价格计算策略
public class BusStrategy implements CalculateStrategy {
@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 {
@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;
}
return 7;
}
}
我们再创建一个扮演角色Context角色的类
//公交出行价格计算器
public class TranficCalculator {
public static void main(String[] args){
TranficCalculator calculator = new TranficCalculator();
// //设置策略模式
calculator.setStrategy(new BusStrategy());
//计算价格
System.out.println("公交车乘16公里的价格:"+calculator.calculatePrice(16));
//设置策略模式
calculator.setStrategy(new TaxiStrategy());
//计算价格
System.out.println("出租车乘16公里的价格:"+calculator.calculatePrice(16));
}
CalculateStrategy mStrategy;
public void setStrategy(CalculateStrategy mStrategy) {
this.mStrategy = mStrategy;
}
public int calculatePrice(int km){
return mStrategy.calculatePrice(km);
}
}
这样代码就重构完成了,代码逻辑非常清晰,而且耦合性降低了,可扩展性变强了,现在如果增加一个出租车的方式,
//出租车计算策略
public class TaxiStrategy implements CalculateStrategy {
@Override
public int calculatePrice(int km) {
return 2 * km;
}
}
总结:
策略模式主要是用来分离算法,在相同的行为抽象下有不同的具体实现策略。这个模式很好地展示了开闭原则,也就是定义抽象,注入不同的实现,从而达到很好的可扩展性。
优点:
- 结构清晰明了,使用简单直观
- 耦合度相对而言较低,扩展方便
操作封装也更为彻底,数据更安全
缺点:
- 随着策略模式的增加,子类也会变得繁多
- 客户端必须知道所有的策略类,并自行决定使用哪一个策略类
- Strategy和Context之间的通信开销
策略模式就到这里,大家对策略模式是不是又有了新的看法呢?
该文章参考《android源码设计模式解析与实战》这本书。