定义
策略模式定义了一系列算法,并将每一个算法封装起来,而且他们还可以相互替换。策略模式让算法独立于使用它的客户而独立变化。
使用场景
- 针对同一类型的问题的多种处理方式,仅仅是具体行为有差别时
- 需要安全的封装多种同一类型的操作时候
- 出现同一抽象类有多个子类,而又需要用if-else或switch-case来选择具体子类时候
模式结构
-
Context 环境类
环境类是使用算法的角色,它在实现某个方法时可以采取多种策略。在环境类中维护着对抽象策略类的引用。
-
Strategy 抽象策略类
为所支持的算法声明抽象方法
-
ConcreteStrategy具体策略类
具体实现了抽象策略类中定义的算法
需求分析
简单实现
以坐车出行为例子,来简单说明,假设选择的交通工具均以路程来计价,我们需要计算最终需支付的金额。
小A要出去玩耍,走好出门,来了辆公交车,小A果断上去坐上了。
public class PriceCalculator {
public static void main(String[] args) {
PriceCalculator calculator = new PriceCalculator();
System.out.println("坐16公里公交车票价:" + calculator.busPrice(16));
}
private int busPrice(int km) {
int extraTotal = km - 10;
// 超出10km部分 每5km需额外付1元 不足5km按照5km计算
int extraFactor = extraTotal / 5;
int fraction = extraTotal % 5;
int price = 1 + extraFactor * 1;
return fraction > 0 ? ++price : price;
}
}
这很简单嘛,我们不假思索就能写出来了。
第二天,小A又要外出了,这时公交车没有来,来了辆出租车,小A选择了坐出租车。
也很简单,只需要在PriceCalculator中添加一个出租车的方法就可以了
public class PriceCalculator {
public static void main(String[] args) {
PriceCalculator calculator = new PriceCalculator();
System.out.println("坐16公里公交车票价:" + calculator.busPrice(16));
System.out.println("坐16公里出租车票价:" + calculator.taxiPrice(16));
}
private int busPrice(int km) {
int extraTotal = km - 10;
// 超出10km部分 每5km需额外付1元 不足5km按照5km计算
int extraFactor = extraTotal / 5;
int fraction = extraTotal % 5;
int price = 1 + extraFactor * 1;
return fraction > 0 ? ++price : price;
}
private int taxiPrice(int km) {
//10km内按起步价10元收费,超出部分每公里收费2元
if (km < 10) {
return 12;
} else {
return 16 + (km - 10) * 2;
}
}
}
第三天,小A想换乘地铁了,同样的,往PriceCalculator中添加一个乘坐地铁的方法subPrice()即可,
System.out.println("坐地铁票价:" + calculator.subPrice(16));
private int subPrice(int km) {
int extraTotal = km - 6;
int extraFactor = extraTotal / 5;
if (km < 6) {
return 3;
} else {
return 3 + extraFactor;
}
}
等等,这样太麻烦了,每次调用一次出行方案,都得调用不同的方法,那如果方法有5个10个,那不就太混乱了?没错,我们可以用一个方法来统一管理出行方案,通过一个字段来判断选择哪种出行方式啊。说干就干
public int calculatorPrice(int km, int type) {
if (type == BUS) {
return busPrice(km);
} else if (type == TAIX) {
return taxiPrice(km);
} else if (type == SUBWAY) {
return subPrice(km);
}
return -1;
}
System.out.println("坐16公里公交车票价:" + calculator.calculatorPrice(16, PriceCalculator.BUS));
System.out.println("坐16公里出租车票价:" + calculator.calculatorPrice(16, PriceCalculator.TAIX));
System.out.println("坐地铁票价:" + calculator.calculatorPrice(16, PriceCalculator.SUBWAY));
现在调用起来比之前方便多了,但也使得calculatorPrice()逻辑变动比较混乱了,这时候还只有3种出行方案,如果需要添加一个新的出行方案,比如坐飞机、比如乘船、比如自驾等等,每多出一个方案就会让这里面的if else 变长,这会使得这个类越来越难以维护。更甚者,如果要取消一种出行方案,涉及到减少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;
// 超出10km部分 每5km需额外付1元 不足5km按照5km计算
int extraFactor = extraTotal / 5;
int fraction = extraTotal % 5;
int price = 1 + extraFactor * 1;
return fraction > 0 ? ++price : price;
}
}
public class TaxiStrategy implements CalculateStrategy{
@Override public int calculatePrice(int km) {
if (km < 10) {
return 12;
} else {
return 16 + (km - 10) * 2;
}
}
}
public class SubwayStrategy implements CalculateStrategy {
@Override public int calculatePrice(int km) {
int extraTotal = km - 6;
int extraFactor = extraTotal / 5;
if (km < 6) {
return 3;
} else {
return 3 + extraFactor;
}
}
}
创建一个扮演Context角色的环境类
public class TrafficCalculator {
CalculateStrategy mStrategy;
public void setStrategy(CalculateStrategy strategy) {
mStrategy = strategy;
}
public int calculatePrice(int km) {
return mStrategy.calculatePrice(km);
}
}
实现测试类
public class Test {
public static void main(String[] args) {
TrafficCalculator calculator = new TrafficCalculator();
calculator.setStrategy(new TaxiStrategy());
calculator.calculatePrice(20);
}
}
这样我们在选择不同的出行方式时候,只需要通过calculator.setStrategy()方法指定对应的方式即可,其他地方的代码基本不需要变动,如果需要增加新的出行方案,也只需要新建一个具体策略类,并且实现CalculateStrategy接口即可。
思考
策略模式简化了逻辑、结构、降低了耦合性,增强了系统的可读性、稳定性与拓展性。合理的利用策略模式,可以有效的减少一部分if else带来的复杂性问题。
在一些简单情况下,用if else能办到的事情就没必要过度设计了,不要滥用设计模式,这点好像也只能从经验中不断摸索了。