20 策略(Strategy)模式
策略模式
1.1 分类
(对象)行为型
1.2 提出问题
做一款打斗游戏,英雄使用不同的武器将会产生不同的损伤效果。
1.3 解决方案
定义一系列算法,把它们一个个封装起来。并且使它们可以相互替换。本模式使得算法可独立于使用它的客户而变化。
1.4 实现类图
- 上下文(Context)维护指向具体策略的引用,且仅通过策略接口与该对象进行交流。
- 策略(Strategy)接口是所有具体策略的通用接口,它声明了一个上下文用于执行策略的方法。
- 具体策略(Concrete Strategies)实现了上下文所用算法的不同变体。
- 当上下文需要运行算法时,它会在其已连接的策略对象上调用执行方法。上下文不清楚其所涉及的策略类型与算法的执行方式。
- 客户端(Client)会创建一个特定策略对象并将其传递给上下文。上下文则会提供一个设置器以便客户端在运行时替换相关联的策略。
1.5 示例代码
#include <iostream>
#include <string>
//Strategy
class Weapon {
public:
virtual std::string fightAlgorithm() const = 0;
};
class Nife : public Weapon {
public:
std::string fightAlgorithm() const override {
return "使用匕首计算损伤。";
}
};
class Axe : public Weapon {
public:
std::string fightAlgorithm() const override {
return "使用斧子计算损伤。";
}
};
//Context
class Hero {
private:
Weapon* m_weapon;
public:
Hero(Weapon* weapon = nullptr) : m_weapon(weapon) {}
void setWeapon(Weapon* weapon) { m_weapon = weapon; }
void fight() {
std::cout << m_weapon->fightAlgorithm() << "\n";
}
};
int main()
{
std::cout << "客户端:英雄使用匕首攻击。\n";
Nife nife;
Hero hero(&nife);
hero.fight();
std::cout << "客户端:英雄使用斧子攻击。\n";
Axe axe;
hero.setWeapon(&axe);
hero.fight();
}
1.6 举个栗子
在导航系统中,不同的出行方式会有不同的导航结果,使用策略模式封装不同的路径规划算法。
1.7 总结
1.7.1 优点
- 可在运行时切换对象内的算法。
- 可将算法的实现和使用算法的代码隔离开来。
- 可使用组合来代替继承。
- 开闭原则。无需对上下文进行修改就能够引入新的策略。
1.7.2 缺点
如果算法极少发生改变,那么没有任何理由引入新的类和接口。使用该模式只会让程序过于复杂。