在一族算法中,每一个算法都单独进行封装,并且各个算法间可以任意替换。策略模式使这些算法的替换动作独立于正在使用这些算法的代码。

在程序设计中,对象常常会根据用户的需求不同而进行多种不同的操作,或者是对象本身的一些行为(方法)会随用户的要求而改变。 

结构图: 

Context类代表操作对象;

Strategy类提供算法(操作、行为)族的抽象接口供Context调用

ConcreteStrategyX代表具体的算法(操作、行为)。 

具体而言,以一个简单的数据排序情景为例:这里需要选择不同的排序算法,对同一段数据进行排序,可供选择的排序算法有插入排序、快速排序和堆排序。 

ArrayData类(Context),是我们需要操作的对象,在其中包含有一个Sort类(Strategy)对象的指针:            

 1 class ArrayData
 2 {
 3 public:
 4                 ArrayData(Sort* s);
 5                 ~ArrayData(void);
 6                 void SortArray(){
 7                     _sort->DoSort(_array);
 8                 };
 9                 Array _array;
10                 void SetAlgorithm(Sort* s){
11                   _sort = s;
12                 }
13 
14 private:   
15             Sort* _sort;
16 };

     Sort类(Strategy)提供了排序算法的接口,因为Sort类本身不提供任何具体的排序算法,因此应该避免创建Sort类的对象,以免产生误操作,所以这里将排序接口函数DoSort()声明为纯虚函数:

1 class Sort
2 {
3  public:
4          Sort(void);
5          ~Sort(void);
6          virtual void DoSort(Array& a) = 0;
7 }; 
    QuickSort类和HeapSort类(ConcreteStrategyX)实现了具体的排序算法(至于InsertSort类,就木有以前写得现成的代码了,所以省略了)。 
 1 class QuickSort :  public Sort
 2 {
 3 public:
 4           QuickSort(void);
 5           ~QuickSort(void);
 6           void DoSort(Array& a){
 7                 QSort(a);//快速排序算法实现
 8            } 
 9 
10 private:
11      //下面是算法实现的具体过程函数,在原来代码的基础上修
12      //改来适应现在的结构,太长,所以省略。           
13 };
14 
15            
16 class HeapSort :    public Sort
17 {
18  public:
19        HeapSort(void);
20        ~HeapSort(void);
21        void DoSort(Array& a){
22              HSort(a);//堆排序算法实现
23        }
24  
25 private:
26         //下面是算法实现的具体过程函数,在原来代码的基础上修
27         //改来适应现在的结构,太长,所以省略。
28 }; 
两个具体的算法实现类完成了两种排序算法工作。他们从Sort类派生出来,实现了一致的DoSort接口函数,因此具有可互换性。
下面来看具体的使用是怎样的,并具有什么好处:      
1 Sort* a = new InsertSort();
2 Sort* b = new QuickSort();
3 Sort* c = new HeapSort();
4       
5 ArrayData* ad = new ArrayData(a);
6 //ad->SetAlgorithm(b);
7 //ad->SetAlgorithm(c);
8 ad->SortArray(); 
以上代码似乎不需要解释了。可以看出,当有需要换用排序算法的时候,只需要将s所指的对象换一个即可,这里使用SetAlgorithm()函数即可完成算法的选择操作。对于其余的代码没有影响。而且,当需要采用新的算法时,只需对Sort类进行派生,就能够很容易的扩展现有的程序,而且对现有代码的改动相当的少。
小结一下:
策略模式的应用场景:
  1、许多仅仅在行为上具有差别的相关类型,策略模式提供了使用其中一种配置一个类的方法;
    2、算法的多样性,见例子;
  3、策略模式对算法的细节进行了封装,使之没有暴露出来,见例子,我们在使用那些算法时,并不需要对算法的细节(流程,数据结构)的了解。
  4、替换条件分支语句:(“Code containing many conditional statements often indicates the need to apply the Strategy pattern.”)
   最后,在应用策略模式的时候,对于最适合的算法的选择工作是要由使用者来决定,因此使用者必须清楚各个算法。并且,策略模式会增加系统中对象的数量(对各个算法都需要一个对象来实现)、潜在的开销(因为各个算法必须实现公共的接口,以提供一致性,所以对一些简单的算法来说,可能存在不必要的对象初始化等等操作。)等问题。
    在实现策略模式的时候,我们还需要考虑的是,对于要Context对象传递进算法类中进行处理的数据(如例子中的Array)是只以参数的形式传递部分成员,还是直接将自身类传递进入算法类,再由算法类来决定如何使用数据成员。前者保持了Context类和Strategy类的低耦合,但有可能传递错误的参数;后者直截方便,但不可避免的增加了耦合度。当然,不用为如何选择疑惑,因为需求,会为你决定一切。

参考资料:
《Head First Design Patterns》
《设计模式——可复用面向对象软件的基础》