8.1 泛化策略模式简述
在上一章的泛化对象工厂中我们知道怎么将一系列带有继承关系的类映射到字符串上,这让我们的代码可以独立于客户端改变行为,但是有时候泛化对象工厂又现得有点捉襟见肘,也许我们想要替换的的行为并没有继承关系,这时候我们就必须想到另外一种设计模式来解决我们的问题。
在【GoF】中描述的策略模式正是我们需要的:“定义一系列算法,把它们一个个封装起来,并且使他们可以互相替换。使得算法可以独立于使用它的客户而变化。”其中并没有提到这些算法必须有什么关系。
其实在【GoF】中将这“一系列算法”都继承自一个基类。不过对于C++,我们能够做得更好,可以让这“一系列算法”都无所有有任何联系。【GoF】中一个例子是我们对文本编辑器的换行操作运用到策略模式就可以针对不同的需要改变不同的策略,并且在增加算法时候不用修改客户端代码。
8.1.1 定义
定义一系列算法,且这些算法没有直接(继承)关系,把它们存储起来,并且使它们可以互相替换。本模式使得算法可独立于使用它的客户端而变化。
8.1.2 泛化目的
对于传统的策略模式仍然有继承关系的局限,没有完全达到策略模式的定义,泛化的目的是总希望减少算法之间的耦合,对算法之间的关系没有苛刻的要求,最大程度的解放程序员。
8.1.3 适用场合
下列场合建议使用泛化策略模式:
l 对于一个算法,我们希望其根据环境的不同而进行改变。
l 算法使用客户不需要知道具体行为,而只需要在不同需要时进行所需替换。
l 当你的程序中出现落后且难于维护的条件判断语句以执行不同算法时。条件判断语句正是面向对象多态想避免的东西,其难于维护,当每添加一个算法(行为)时候总是需要修改客户端代码。
8.1.4 实作泛化策略模式前的思考
C++之所以迷人就是因为其无所不能的灵活,C++从来都在告诉所有藐视其语言庞杂性的程序员它是无所不能的。在我程序设计过程中遇到过很多连工作了多年的其他语言朋友也感觉不可能的解决的问题,例如用一个容器存储不同数据类型、用一个容器存储不同数据结构,不过只要你有不放弃的精神,就总能在C++中找到解决的方法,至少C++可以做到很多看似神奇的工作。
当若干算法(行为)不具有继承关系时候我们将怎么存储是一个难题,前面的对象工厂里面介绍时候的第一个实作品是一个存储对象的容器,但是必须是这些对象(算法,行为)必须具有继承自相同的基类的特点。如果所有行为没有绝对的关系,那我们将无法用一个类似链表或者关联容器去存储,这就强迫我们必须自己设计解决算法。
最好的办法,也是我们用了很多次的办法就是利用多态中基类对继承类新成员的“一无所知”解决问题。思考解决问题的方法是需要一定得程序经验积累,需要大脑的不断思考。下面我们开始看看怎么存储不同类型的行为算法。
8.1.5 行为算法的定义
在C++中通常是函数来代表一个行为算法,不过由于函数作为参数将带来极大地安全隐患(函数指针不具有value语义),所以在我这的算法是希望用泛化仿函数作为行为算法的载体,当然泛化仿函数有一个好处就是同样兼容函数指针(详见第五章)。
8.1.6 实作行为容器
首先我们需要创建三个类:
l BaseStrategy(虚基类,定义行为调用函数)
l Strategy(继承自BaseStrategy,定义了一个泛化对象工厂以存储其继承类,这句话会比较难理解,请看后面代码)
l ConcreteStrategy(继承自Strategy,存储行为)
这样的结构好处就是Stratrgy对ConcreteStrategy中存储的行为可能一无
所知,但是却能通过虚函数得到。BaseStrategy的存在完全是为了应付行为参数不同的重载(可参考第五章泛化仿函数实作)。
下面看看具体代码,需要声明的是出于控制文章长度考虑,我假设行为参数至多只有两个,如果再添加更多参数只需要拷贝修改前面的代码。需要注意的是对算法的映射我依然采用算法名字的字符串映射。
//和往常一样,ResultType为返回值类型,TVector为参数类型
template<typename ResultType,typename TVector>
class BaseStrategy
{
};
//下面这是对没有参数的行为的偏特化体
template<typename ResultType>
class BaseStrategy<ResultType,TypeVector<> >