Strategy 策略模式
意图
定义一系列算法,把它们一个个封装起来,并且使他们可以互相替换。本模式使得算法可以独立于它的客户而变化。
Strategy模式将算法独立于程序流程之外,降低了算法与程序主流程之间的耦合度。
- 将算法硬编入程序主流程段(和程序主流程写在一起),会导致程序难以维护。日后再添加新的算法时,也不利于扩展。
- 并且,通常我们只想用一部分算法来支持我们的程序,比如提供的99个算法我们只用到2-3个,那么全部编写在一起会造成内存的大量浪费。当然这只是次要问题,主要要解决的,还是降低程序耦合度。
从稳定-变化的角度来分析的话,使用Strategy模式的程序段,算法本身涉及到增删和变化,但算法段之外的流程保持稳定。
代码案例
enum class Sins
{
MURDER, STEAL, ROBBERY, DEFRAUD
}
void judge(Sins sin)
{
//判罪前的通用流程
...
//处理算法
if(sin == MURDER) {...}
else if(sin == STEAL) {...}
else if(sin == ROBBERY) {...}
else if(sin == DEFRAUD) {...}
//判罪后的通用流程
...
}
如上所示,这个例子是一个判罪的程序,“算法” 即针对不同的罪行进行相应的处理(判10年,20年,无期之类的,这只是一个示例,因此省去了大部分细节)。
在这个程序段中,为罪行指定刑罚之前和之后的流程是稳定的,比如,判罪前要搜集罪证啊,检察官提起公诉啊,判罪后要登记在案,罪犯可能会上诉啊之类的。而针对罪行的判定是不稳定的,经常会提出一些修正案,增删或变更一些条款。
考虑这种情况,现在我们需要增加一种算法(也就是增加一种罪名),需要做什么呢?我们需要在ENUM中增加罪名,并且在Judge函数中增加相应的处理代码段。显然,代码的复用性并不好,每次对算法的改变都需要重新编译Judge函数。
从设计原则的角度来讲,这种写法违背了开放封闭原则(OCP)(详情可在文章开头 设计模式学习:概述 中了解)。降低了可扩展性。并且,有可能带来debug的问题:每次对算法的改变都有可能在judge函数内部造成Bug。
那么,现在我们用strategy模式改写一下:
class SinStrategy
{
virtual void dealSin() = 0;
virtual ~SinStrategy(){};//C++中基类的析构函数要写成虚的!这里提醒大家。
}
class MurderStrategy : public SinStrategy
{
//override
void dealSin() {...}
}
class StealStrategy : public SinStrategy
{
//override
void dealSin() {...}
}
class RobberyStrategy : public SinStrategy
{
//override
void dealSin() {...}
}
void judge(SinStrategy* sin)
{
//判罪前的通用流程
...
//处理算法,动态绑定
sin -> dealSin();
//判罪后的通用流程
...
}
如上,SinStrategy类作为抽象基类,定义了一个虚函数dealSin,这个函数就是我们要封装的算法。之后的每种罪行只需要重写这一个函数就可以了
而在judge函数中,我们采用动态绑定的方式。程序不知道到底是那种罪名,只需要按规定的流程走完即可。关于SinStrategy具体指向那种子类,就不是我们这个函数操心的问题了。
可以这样调用Judge函数
judge(new StealStrategry());
judge(new MurderStrategry());
judge(new RobberyStrategry());
就实现了对不同罪行的分开处理,而处理罪行的算法也成功的和流程代码段解耦,实现了松耦合。
解释
在strategy模式中,我们把一个特定的算法看作一个对象,因此就可以对它应用多态的思想。
可以这样简单的理解:只要在程序中看到if-else语句或switch语句,就要思考这里需不需要应用Strategy模式——Strategy模式相当于将if-else, switch-case中的模块封装到一个个对象中。至于要不要用Strategy替换If-else/switch,取决于你自己的判断。
Strategy模式也是一个十分常见的设计模式,本质思想和TemPlate Method类似,都是为了遵循开放封闭原则,对某些代码段进行封装,只是对象不同罢了。
总结
设计模式 | Strategy(策略模式) |
---|---|
稳定点: | 程序的整体运行框架 |
变化点: | 子算法 |
效果: | 使得可以独立于主程序增删修改算法 |
特点: | 封装算法,重写子过程 |
2020.1.26 转载请标明出处