文章目录
- 本质:
- 动机:
- 定义:
- 一个不好的例子
- 策略模式重写
- 总结
本质:
分离算法,选择实现。
动机:
在软件构建过程中,某些对象使用的算法可能多种多样,经常改变,如果将这些算法都编码到对象中,将会使对象变得异常复杂;而且有时候不支持使用的算法也是一个性能负担
如何在运行时根据需要透明地更改对象的算法,将算法与对象本身解耦,从而避免上述的问题?
定义:
定义一系列算法,把它们一个个封装起来,并且使它们可互相替换(变化)。该模式使得算法可独立于使用它的客户程序(稳定)而变化(扩展、子类化)
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(策略模式) |
---|---|
稳定点: | 程序的整体运行框架 |
变化点: | 子算法 |
效果: | 使得可以独立于主程序增删修改算法 |
特点: | 封装算法,重写子过程 |
UML类图
要点
Strategy 及其子类为组件提供了一系列可重用的算法,从而可以使得类型在运行时方便地根据需要在各个算法之间进行切换
Strategy 模式提供了用条件判断语句以外的另一种选择,消除条件判断语句,就是在解耦合。含有许多条件判断语句的代码通常都需要 Strategy 模式
如果Strategy 对象没有实例变量,那么各个上下文可以共享同一个Strategy 对象,从而节省对象开销
参考一:https://blog.csdn.net/natrick/article/details/113174673
参考二:https://blog.csdn.net/mg2flyingff/article/details/105310045