1.类图和概念
Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it.(定义一组算法,将每个算法都封装起来,并且使它们之间可以互换。)
策略模式的三个角色:
- Context封装角色:也叫上下文角色,屏蔽高层模块对策略、算法的直接访问,封装可能存在的变化。
- Strategy抽象策略角色:策略、算法家族的抽象,一般是接口。
- ConcreteStrategy具体角色策略:实现抽象策略中的操作,该类含有具体的算法。
2.代码
抽象策略角色代码:
public interface Strategy {
public void doSomething();
}
具体策略也是一个非常普通的实现类,具体策略角色代码:
public class ConcreteStrategy1 implements Strategy {
public void doSomething() {
System.out.println("具体策略1的算法");
}
}
public class ConcreteStrategy2 implements Strategy {
public void doSomething() {
System.out.println("具体策略2的算法");
}
}
封装角色代码:
public class Context {
private Strategy strategy = null;
public Context(Strategy strategy_) {
this.strategy = strategy_;
}
public void doSomething() {
strategy.doSomething();
}
}
高层模块的代码:
Strategy strategy = new ConcreteStrategy1();
//传入不同的策略,多态执行不同的策略算法
Context context = new Context(strategy);
context.doSomething();
3.策略模式的使用场景
在下面的情况下应当考虑使用策略模式:
- 如果在一个系统里有很多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。
- 一个系统需要动态地在几种算法中选择一种。
- 一个系统的算法使用的数据不可以让客户端知道。策略模式可以避免让客户端涉及到不必要接触到的复杂的和只与算法有关的数据。
- 如果一个对象有很多行为,如果不用恰当的模式,这些行为就只好使用多重条件选择语句来实现。此时,使用策略模式,把这些行为转移到相应的具体策略类里面,就可以避免使用难以维护的多重条件选择语句。
4.策略的选择
有同事问我一个问题:为什么不能从策略模式中看出哪一个具体策略适用于哪一种情况呢?
答案很简单,策略模式不负责做这个决定。也就是说,应当由客户端自己决定在什么情况下使用什么具体策略角色。策略模式仅仅封装算法,并提供新算法插入到已有系统中,策略模式并不决定在何时使用何种算法。
5.策略模式的扩展
考虑一道小学题目:输入3个参数,进行加减运算,参数中两个是int的,剩下一个是String类型的,只有“+”、“-“两个符号可以选择。很简单,我们先写一个实现,代码如下:
public class Calculator {
private final static String ADD_SYMBOL = "+";
private final static String SUB_SYMBOL = "-";
public int exec(int a, int b, String symbol) {
return (symbol.equals(ADD_SYMBOL) ? a + b : a - b);
}
}
使用策略模式再来实现一下:
public interface Calculator {
public int exec(int a, int b);
}
public class Add implements Calculator {
public int exec(int a, int b) {
return a + b;
}
}
public class Sub implements Calculator {
public int exec(int a, int b) {
return a - b;
}
}
public class Context {
private Calculator cal = null;
public Context(Calculator _cal) {
cal = _cal;
}
public int exec(int a, int b) {
return cal.exec(a, b);
}
}
代码也很简单,只是使用了策略模式封装起来了。再看一个实现,使用枚举,代码如下:
public enum Calculator1 {
ADD("+") {
public int exec(int a, int b) {
return a + b;
}
},
SUB("-") {
public int exec(int a, int b) {
return a - b;
}
};
private String value = "";
private Calculator1(String _value) {
value = _value;
}
public String getValue() {
return value;
}
public abstract int exec(int a, int b);
}
首先,这是一个枚举,然后,定义了一个抽象方法exec(), 在每个枚举成员中进行了实现。也就是说,每个枚举成员成为一个具体策略。
调用代码如下:
Calculator1.Add(1, 2);
Calculator2.Sub(1, 2);
策略枚举是一个非常优秀和方便的模式,但是它受枚举类型的限制,每个枚举类型都是public、final、static的,扩展性受到了一定的约束,因此在系统开发中,策略枚举一般担当不经常发生变化的角色。