1 定义:
1.1 定义:Define a family of algorithms, encapsulate each one, and make them interchangeable.(定义一组算法,将每个算法都封装起来,并且使它们之间可以互换)。
1.2 通用类图:
角色解说:
Context封装角色:也叫上下文角色,屏蔽高层模块对策略、算法的直接访问,封装可能的变化。
Stategy抽象策略角色:策略、算法家族的抽象,通常为接口,定义每个策略或算法必须具有的方法和属性。
ConcreteStrategy具体策略角色:实现抽象策略中的操作,该类含有具体的算法。
1.3 通用代码:
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 doAnythinig() {
this.strategy.doSomething();
}
}
public class Client {
public static void main(String[] args) {
// 声明出一个具体的策略
Strategy strategy = new ConcreteStrategy1();
// 声明出上下文对象
Context context = new Context(strategy);
// 执行封装后的方法
context.doAnythinig();
}
}
针对不同的算法,直接根据算法new一个新的Context,即可,不用做别的。
2 优点
2.1 算法可以自由切换;
2.2 避免多重条件判断:策略模式中,可以由其他模块决定采用何种策略,策略家族对外提供的访问接口就是封装类(上下文),简化了操作,同时避免了条件语句判断;
2.3 扩展性良好:添加策略时,只要实现接口就可以了,其他都不用修改,符OCP。
3 缺点
3.1 策略类数量增多:但复用的可能性很小;
3.2 所有的策略类都需要对外暴露:上层模块必须知道有哪些策略,然后才能决定使用哪一个策略,这与LOW法则相违背(只是想使用一个策略,为什么要了解它呢?)。那要封装类作何?这原是策略模式的一个缺点。可以使用工厂方法模式、代理模式或享元模式(应该是使用这些模式,直接返回一个已封装了某一策略的Context对象)。
4 应用场景
4.1 多个类只有在算法或行为上稍有不同的场景;
4.2 算法需要自由切换的场景:算法的选择由使用者决定,或算法始终在进化。
4.3 需要屏蔽算法规则的场景:我只想记住算法名,给我反馈结果即可。
5 注意事项
一个策略家族的具体策略数量超过4个,则需要考虑混合模式,解决策略类膨胀和对外暴露的问题,否则日后的系统维护太难。
6 扩展
6.1 策略枚举:在枚举里定义抽象方法,并在其内部实现若干实例。(但是扩展性不好,因为它受枚举类型的限制,每个枚举项都是public,final,static的,扩展性受到了一定的约束,因此在系统开发中,策略枚举一般担当不起经常发生变化的角色。)
可以根据3.2,结合7.2例,来由其它模式提供算法的封装。
7 范例(作者原书例)
7.1 策略模式实现加减法:
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) {
this.cal = _cal;
}
public int exec(int a, int b, String symbol) {
return this.cal.exec(a, b);
}
}
public class Client {
// 加符号
public final static String ADD_SYMBOL = "+";
// 减符号
public final static String SUB_SYMBOL = "-";
public static void main(String[] args) {
// 输入的两个参数是数字
int a = Integer.parseInt(args[0]);
String symbol = args[1]; // 符号
int b = Integer.parseInt(args[2]);
System.out.println("输入的参数为:" + Arrays.toString(args));
// 上下文
Context context = null;
// 判断初始化哪一个策略
if (symbol.equals(ADD_SYMBOL)) {
context = new Context(new Add());
} else if (symbol.equals(SUB_SYMBOL)) {
context = new Context(new Sub());
}
System.out.println("运行结果为:" + a + symbol + b + "="
+ context.exec(a, b, symbol));
}
}
7.2 枚举策略实现加减法:
public enum Calculator { //加法运算 ADD("+"){ public int exec(int a,int b){ return a+b; } }, //减法运算 SUB("-"){ public int exec(int a,int b){ return a - b; } }; String value = ""; //定义成员值类型 private Calculator(String _value){ this.value = _value; } //获得枚举成员的值 public String getValue(){ return this.value; } //声明一个抽象函数 public abstract int exec(int a,int b); } import java.util.Arrays; /** * @author cbf4Life [email protected] * I'm glad to share my knowledge with you all. */ public class Client { public static void main(String[] args) { //输入的两个参数是数字 int a = Integer.parseInt(args[0]); String symbol = args[1]; //符号 int b = Integer.parseInt(args[2]); System.out.println("输入的参数为:"+Arrays.toString(args)); System.out.println("运行结果为:"+a + symbol + b + "=" + Calculator.ADD.exec(a, b)); } }