在一族算法中,每一个算法都单独进行封装,并且各个算法间可以任意替换。策略模式使这些算法的替换动作独立于正在使用这些算法的代码。
在程序设计中,对象常常会根据用户的需求不同而进行多种不同的操作,或者是对象本身的一些行为(方法)会随用户的要求而改变。
结构图:

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》
《设计模式——可复用面向对象软件的基础》