策略模式
针对同一类情况, 如果因为某个或多个因素和有不同的处理方法, 一般这种就会造成写很多的if
else
, 甚至嵌套. 策略模式就是针对这种情况的一种让代码遵循开闭原则的方案.
适用场景
- 针对同一类型问题的多种处理方式, 仅仅是具体行为有差别时
- 需要安全的封装同一类型的操作时
- 出现同一个抽象有多个子类, 而又要使用
if
else
来判断具体实现时
类图
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| @startuml
class Context { +algorithm() +setStrategy(Strategy) }
interface Strategy { +algorithm() }
class StrategyA { +algorithm() }
class StrategyB { +algorithm() }
Context o- Strategy
Strategy <|-- StrategyA Strategy <|-- StrategyB
@enduml
|
- Context: 持有策略的上下文
- Strategy: 策略的抽象
- StrategyA, StrategyB 具体的策略实现
优点
通过将同一类型问题的处理方式抽象, 然后由具体类实现行为. 在需要使用不同的方式处理时, 将具体实现类注入到持有策略的上下文中. 避免将不同处理方式通过 if
else
实现在一个类中.
例子
计算在民用电和工业用电下, 使用相同电, 需要缴纳的电费. 因为民用电和工业电的计算方法不一样, 所以会有分支出现.
常规实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| public class ElectricCalculator {
enum Type { V220, V380 }
public double calculate(float deg, Type type) { if (type == Type.V220) { return calculateV220(deg); } if (type == Type.V380) { return calculateV380(deg); } return 0; }
private double calculateV220(float deg) { return deg * 0.5; }
private double calculateV380(float deg) { return deg * 0.86; }
public static void main(String[] args) { ElectricCalculator calculator = new ElectricCalculator(); double feeV220 = calculator.calculate(100, Type.V220); double feeV380 = calculator.calculate(100, Type.V380); } }
|
现在这个类里的逻辑还比较简单, 看起来没什么问题. 如果加上电费的梯度收费、分季节收费, 再加上支持租房用电费计算, 那这个类的逻辑就有点乱了.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| public class ElectricCalculator {
enum Type { V220, V380, RENT }
public double calculate(float deg, Type type, boolean isSummer) { if (type == Type.V220) { return calculateV220(deg, isSummer); } if (type == Type.V380) { return calculateV380(deg, isSummer); } if (type == Type.RENT) { return calculateRent(deg, isSummer); } return 0; }
private double calculateV220(float deg, boolean isSummer) { float rate = 0.5f; if (deg > 500) { rate = 0.6f; } if (deg > 800) { rate = 0.7f; } if (isSummer) { rate += 0.1f; } return deg * rate; }
private double calculateV380(float deg, boolean isSummer) { return deg * 0.86; }
private double calculateRent(float deg, boolean isSummer) { return deg * 1; } }
|
现在支持了梯度收费和分季节收费, 而且每个类型的电费计算方式又不一样, 这个类里就会包含很多的分支逻辑. 且当需要修改或添加一种电费类型时, 很容易造成错误.
这种情况下, 就适合使用策略模式, 将电费的计算方法抽象化, ElectricCalculator
中的计算方法的具体实现由外部注入.
重构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| @startuml
class ElectricCalculator { +calculate(int deg) +setCalculator(Calculator) }
interface Calculator { +calculate(int deg) }
class V220Calculator { +calculate(int deg) }
class V220SummerCalculator { +calculate(int deg) }
ElectricCalculator o- Calculator
Calculator <|-- V220Calculator Calculator <|-- V220SummerCalculator
@enduml
|
策略抽象
1 2 3
| interface Calculator { double calculate(float deg); }
|
实现不同策略
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| class V220Calculator implements Calculator {
@Override public double calculate(float deg) { float rate = 0.5f; if (deg > 600) { rate = 0.6f; } if (deg > 900) { rate = 0.7f; } return deg * rate; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| class V220SummerCalculator implements Calculator {
@Override public double calculate(float deg) { float rate = 0.5f; if (deg > 500) { rate = 0.6f; } if (deg > 800) { rate = 0.7f; } return deg * rate; } }
|
注入策略
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public class ElectricCalculator { private Calculator mCalculator;
public void setCalculator(Calculator calculator) { mCalculator = calculator; }
public double calculate(float deg) { if (mCalculator == null) { throw new IllegalStateException("Calculator must not null"); } return mCalculator.calculate(deg); }
public static void main(String[] args){ ElectricCalculator calculator = new ElectricCalculator(); calculator.setCalculator(new V220Calculator()); double fee = calculator.calculate(100); calculator.setCalculator(new V220SummerCalculator()); double feeSummer = calculator.calculate(10); } }
|
重构之后
可以看出, ElectricCalculator
类中的逻辑变简单了, 而且每个不同的分支由不同的策略实现类代替, 保证了增加新策略的扩展性和修改代码的封闭性(不用修改ElectricCalculator
中代码).