也称为 Policy 模式。
定义一系列算法,把他们一个个封装起来,并且使他们可以相互替换。该模式使得算法可以独立于使用它的客户而变化。
“Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it.”- GoF
在软件构建过程中,某些对象使用的算法可能多种多样,经常改变,如果将这些算法都编码到某个对象中,将会使该对象变得异常复杂;而且有时候支持不使用的算法也是一个性能负担。Strategy设计模式就是在运行时根据需要透明地更改对象的算法,将算法和对象本身解耦。
我们在编程中,经常碰到这样的情况:用不同的办法去做同一件事情,比如,
1. 将文件保存为不同的格式;
2. 用不同的压缩算法压缩一个文本文件;
3. 用不同的压缩算法压缩视频文件
4. 将同样的数据,用不同的图形显示出来:折线图、柱状图或者饼图;
5. 将用某种语言写的一个句子,翻译成为几种其他的语言。
客户程序告诉驱动模块(不妨称为Context),它将使用哪个算法(即所谓的strategy或policy),并请求Context执行该算法。
Strategy模式UML类图:
v
角色:
Strategy:
- 给所有支持的算法声明一个通用接口,Context使用该接口调用由ConcreteStrategy定义的算法。
ConcreteStrategy:
- 按照Strategy给出的接口实现具体的算法。
Context
- 包含一个Strategy的引用;
- 可以由ConcreteStrategy对象对其进行配置。
业务示例:
将阿拉伯数字“520”分别翻译成中文、英语和俄语。
下面是具体C++代码:
// Strategy.h
#include <iostream>
#include <string>
#include <memory>
using namespace std;
// Strategy抽象类,用做借口
class Strategy
{
public:
virtual string substitute(string str) = 0;
public:
virtual ~Strategy()
{
cout << "in the destructor of Strategy..." << endl;
}
};
// 中文Strategy
class ChineseStrategy : public Strategy
{
public:
string substitute(string str)
{
size_t index = str.find("520");
string tempstr = str.replace(index, 3, "我爱你");
return tempstr;
}
public:
~ChineseStrategy()
{
cout << "in the destructor of ChineseStrategy..." << endl;
}
};
// 英语Strategy
class EnglishStrategy : public Strategy
{
public:
string substitute(string str)
{
size_t index = str.find("520");
string tempstr = str.replace(index, 3, "I love you");
return tempstr;
}
public:
~EnglishStrategy()
{
cout << "in the destructor of EnglishStrategy..." << endl;
}
};
// 俄语Strategy
class RussianStrategy : public Strategy
{
public:
string substitute(string str)
{
size_t index = str.find("520");
string tempstr = str.replace(index, 3, "Я люблю тебя");
return tempstr;
}
public:
~RussianStrategy()
{
cout << "in the destructor of RussiaStrategy..." << endl;
}
};
// Context类
class Translator
{
private:
auto_ptr<Strategy> strategy;
public:
~Translator()
{
cout << "in the destructor of Translator..." << endl;
}
public:
void set_strategy(auto_ptr<Strategy> strategy)
{
this->strategy = strategy;
}
string translate(string str)
{
if(0 == strategy.get()) return "";
return strategy->substitute(str);
}
};
// Strategy.cpp
#include "Strategy.h"
int main(int argc, char** argv)
{
string str("321520");
Translator* translator = new Translator;
// 未指定strategy时
cout << "No strategy: " << translator->translate(str) << endl;
cout << "--------------------------" << endl;
// 翻译成中文
auto_ptr<Strategy> s1(new ChineseStrategy());
translator->set_strategy(s1);
cout << "Chinese strategy: " << translator->translate(str) << endl;
cout << "--------------------------" << endl;
// 翻译成英语
auto_ptr<Strategy> s2(new EnglishStrategy());
translator->set_strategy(s2);
cout << "English strategy: " << translator->translate(str) << endl;
cout << "--------------------------" << endl;
// 翻译成俄语
auto_ptr<Strategy> s3(new RussianStrategy());
translator->set_strategy(s3);
cout << "Russian strategy: " << translator->translate(str) << endl;
cout << "--------------------------" << endl;
delete translator;
return 0;
}
运行结果:
No strategy:
--------------------------
Chinese strategy: 321我爱你
--------------------------
in the destructor of ChineseStrategy...
in the destructor of Strategy...
English strategy: 321I love you
--------------------------
in the destructor of EnglishStrategy...
in the destructor of Strategy...
Russian strategy: 321Я люблю тебя
--------------------------
in the destructor of Translator...
in the destructor of RussiaStrategy...
in the destructor of Strategy...
结果符合预期。
1. 从上面程序可以很容易看出,可以在运行时改变translator对象的行为;
2. 算法如果改变了,客户端程序不需要做任何改动,比如在EnglishStrategy的实现中将“520”翻译成“Five hundred and twenty”,客户端代码无需任何改变;
3. 当需要增加新的算法时,比如要将“520”翻译成日语的“わたし爱してるあなた”,只需要增加一个具体的Strategy类,比如JapaneseStrategy类,并重写Strategy中声明的纯虚函数即可。这完全符合OCP。
上述代码的UML类图:
Strategy模式和State粗看起来非常相似,但他们的意图完全不同。他们主要的区别在于:
1. Strategy一次只能选择一个strategy(即算法),而State模式中,不同的状态有可能同时被激活;
2. Strategy封装的是算法,State封装的是状态;
3. Strategy所封装的算法(每个算法对应一个类),所做的事情相差无几,而State所封装的状态(每个状态对应一个类),往往颇不相同;
4. State模式中的状态迁移的概念,在Strategy中根本不存在。