策略模式

策略模式使用的就是面向对象的继承和多态机制。

定义

定义一组算法,将每个算法都封装起来,并且使它们之间可以互换。

策略模式通用类图

实现

Strategy 抽象策略角色

策略算法的抽象,通常是接口,定义每个策略或者算法必须具有的方法和属性。

1
2
3
public interface Strategy{
void doSomething();
}

ConcreteStrategy 具体策略角色

实现抽象策略的具体算法。

1
2
3
4
5
6
7
8
9
10
11
public class ConcreteStrategy1 implements Strategy{
public void doSomething(){
//Do Strategy1.
}
}

public class ConcreteStrategy2 implements Strategy{
public void doSomething(){
//Do Strategy2.
}
}

Context 封装角色

封装上下文,屏蔽高层模块对策略、算法的直接访问,封装可能存在的变化。
策略模式的重点就是封装角色,它借用了代理模式的思路。

1
2
3
4
5
6
7
8
9
public class Context{
private Strategy strategy = null;
public Context(Strategy strategy){
this.strategy = strategy;
}
public void doAny(){
this.strategy.doSomething();
}
}

Use

1
2
3
Strategy strategy = new ConcreteStrategy1();
Context context = new Context(strategy);
context.doAny(strategy);

应用

优点

  • 算法可以自由切换
    只需要实现策略接口类,通过封装角色对其进行封装即可。
  • 避免使用多重条件判断
    1
    2
    3
    4
    5
    6
    if (condition1){
    strategy1.do();
    }else if(condition2){
    strategy2.do();
    }
    // .....
  • 扩展性好

缺点

  • 策略类数量多
  • 所有策略类都需要对外暴露
    上层模块必须知道有哪些策略, 然后才能决定使用哪一个策略,与迪米特法则相违背。
    (迪米特法则:一个类对于其他类知道的越少越好)

注意事项

当具体的策略数量超过一定个数时,考虑使用混合模式,解决策略类膨胀和对外暴露的问题,否则会带来很大的系统维护成本。

使用场景

  • 多个类只有在算法或者行为上稍有不同的场景
  • 算法需要自由切换的场景
  • 需要屏蔽算法规则的场景

策略枚举

当策略枚举不经常发生变化时可以使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public enum StrategyEnum {

ADD("+") {
@Override
public int exec(int a, int b) {
return a + b;
}
},
SUB("-") {
@Override
public int exec(int a, int b) {
return a - b;
}
};

private String opt;
StrategyEnum(String opt) {
this.opt = opt;
}

abstract int exec(int a, int b);
}