策略模式(Strategy):定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。策略模式使得算法可独立于使用它的客户而变化。

适用场景:

1、许多相关的类仅仅是行为有异。“策略”提供了一种用多个行为中的一个行为来配置一个类的方法;

2、需要使用一个算法的不同变体;

3、算法使用客户不应该知道的数据。可使用策略模式以避免暴露复杂的、与算法相关的数据结构;

4、一个类定义了多种行为, 并且这些行为在这个类的操作中以多个条件语句的形式出现。将相关的条件分支移入它们各自的Strategy 类中以代替这些条件语句。

通用类图:

Strategy

 

实际上,策略模式是采用了面向对象的继承和多态机制,客户根据自己的业务需要调用上下文(Context 类)获得相应的具体策略、算法实现。

假设我们要实现一个非常简单的计算机,只能进行加减操作。一般我们会这样设计:根据传入的 String 型参数使用 if… else… 语句判断用户需要哪种操作。

代码如下:

 
  
  1. class Calculator {  
  2.     private final static String ADD = "+";  
  3.     private final static String SUB = "-";  
  4.       
  5.     public int calculate(int a, int b, String operator) {  
  6.         int result = 0;  
  7.         if(this.ADD.equals(operator)) {  
  8.             result = a + b;  
  9.         }else {  
  10.             result = a - b;  
  11.         }  
  12.         return result;  
  13.     }  
  14. }  
  15.  
  16. // 测试类  
  17. public class Client {  
  18.     public static void main(String[] args) {  
  19.         Calculator calculator = new Calculator();  
  20.         System.out.println(calculator.calculate(1013"+"));  
  21.         System.out.println(calculator.calculate(1013"-"));  
  22.     }  
这样的设计比较死板,如果我们要实现乘除法呢?就不得不修改 Calculator  类中的 operation()方法,改变 if… else…语句的结构。 为了适应变化,使用策略模式来重新设计。

代码如下:

 
  
  1. // 定义算法框架,具体的算法由子类实现  
  2. interface Operation {  
  3.     public int calculate(int a, int b);  
  4. }  
  5.  
  6. class Add implements Operation {  
  7.     public int calculate(int a, int b) {  
  8.         return (a + b);  
  9.     }  
  10. }  
  11.  
  12. class Sub implements Operation {  
  13.     public int calculate(int a, int b) {  
  14.         return (a - b);  
  15.     }  
  16. }  
  17.  
  18. // 上下文 Context 类  
  19. class Calculator {  
  20.     private Operation operation;  
  21.       
  22.     // 设置具体的算法策略  
  23.     public void setOperation (Operation operation) {  
  24.         this.operation = operation;  
  25.     }  
  26.       
  27.     public int calculate(int a, int b) {  
  28.         return this.operation.calculate(a, b);  
  29.     }  
  30. }  
  31.  
  32. // 测试类  
  33. public class Client {  
  34.     public static void main(String[] args) {  
  35.           
  36.         Calculator calculator = new Calculator();  
  37.           
  38.         // 加法操作  
  39.         calculator.setOperation(new Add());  
  40.         System.out.println(calculator.calculate(2013));  
  41.           
  42.         // 减法操作  
  43.         calculator.setOperation(new Sub());  
  44.         System.out.println(calculator.calculate(2013));  
  45.     }  

如果此时我们需要添加乘除法,只需要再写一个实现了 Operation 接口的类,并实现其具体策略即可,在客户端即可方便地调用。 

下面使用枚举策略的方式来实现,代码如下:

 
  
  1. // 策略枚举  
  2. enum Calculator {  
  3.       
  4.     // 加法策略的实现  
  5.     Add(){  
  6.         public int calculate(int a, int b) {  
  7.             return (a + b);  
  8.         }  
  9.     },  // 这里用逗号隔开各个枚举变量  
  10.       
  11.     // 减法策略的实现  
  12.     Sub(){  
  13.         public int calculate(int a, int b) {  
  14.             return (a - b);  
  15.         }  
  16.     }; // 这里用逗号结束枚举变量的定义  
  17.       
  18.     // 定义抽象算法方法,让每个枚举变量来具体实现  
  19.     public abstract int calculate(int a, int b);  
  20. }  
  21.  
  22. public class Client {  
  23.     public static void main(String[] args) {  
  24.         // 加法  
  25.         System.out.println(Calculator.Add.calculate(2013));  
  26.           
  27.         // 减法  
  28.         System.out.println(Calculator.Sub.calculate(2013));  
  29.     }  

使用枚举策略模式,不足是并不能像上面第二种方式那样适应变化,原因是如果要添加其他策略,同样需要修改枚举类,即添加具体的枚举变量。不过枚举策略的代码结构确实比较简洁。由于枚举策略的模式也不适合扩展,因此适合担任不会频繁发生变化的角色。
 

策略模式是比较简单的一个模式了,但是它也有不足之处:所有的具体策略都必须暴露给客户,否则客户怎样根据自身需要使用不同的策略呢?而且,策略模式实际上是把条件判断的逻辑(即各个条件分支,也就是每个具体策略)转移到客户端了,由客户来选择不同的具体策略。