概述:
每个人都要“交个人所得税”,但是“在美国交个人所得税”和“在中国交个人所得税”就有不同的算税方法。 而策略模式就是对算法进行包装,是把使用算法的责任和算法本身分割开来,委派给不同的对象管理。策略模式通常把一个系列的算法包装到一系列的策略类里面,作为一个抽象策略类的子类。用一句话来说,就是:“准备一组算法,并将每一个算法封装起来,使得它们可以互换”。
类图与实例:
Context(应用场景):
1、需要使用ConcreteStrategy提供的算法。
2、内部维护一个Strategy的实例。
3、负责动态设置运行时Strategy具体的实现算法。
4、负责跟Strategy之间的交互和数据传递。
Strategy(抽象策略类):定义了一个公共接口,各种不同的算法以不同的方式实现这个接口,Context使用这个接口调用不同的算法,一般使用接口或抽象类实现。
ConcreteStrategy(具体策略类):实现了Strategy定义的接口,提供具体的算法实现。
这里用CS里的人物作为例子,每个人都可以有几个武器,武器之间动态切换,武器拥有统一的攻击命令:
#include <iostream> using namespace std; class WeaponBehavior { public: void virtual useWeapon() = 0; }; class AK47:public WeaponBehavior { public: void useWeapon() { cout << "Use AK47 to shoot!" << endl; } }; class Knife:public WeaponBehavior { public: void useWeapon() { cout << "Use Knife to kill!" << endl; } }; class Character { public: Character():m_weapon(nullptr){} void setWeapon(WeaponBehavior *w) { this->m_weapon = w; } void virtual fight() = 0; protected: WeaponBehavior *m_weapon; }; class King:public Character { public: void fight() { cout << "The king:" ; if ( this->m_weapon == nullptr) { cout << "You don't have a weapon! Please Set Weapon!" << endl; } else { m_weapon->useWeapon(); } } }; int main() { WeaponBehavior *ak47 = new AK47(); WeaponBehavior *knife = new Knife(); Character *kin = new King(); kin->fight(); cout << endl; kin->setWeapon(ak47); kin->fight(); cout << endl; kin->setWeapon(knife); kin->fight(); return 0; }适用性:
1,多个类只区别在表现行为不同,可以使用Strategy模式,在运行时动态选择具体要执行的行为。
2,需要在不同情况下使用不同的策略(算法),或者策略还可能在未来用其它方式来实现。
3,对客户隐藏具体策略(算法)的实现细节,彼此完全独立。
优缺点:
优点:
1,策略模式提供了管理相关的算法族的办法。策略类的等级结构定义了一个算法或行为族。恰当使用继承可以把公共的代码移到父类里面,从而避免代码重复。
2,使用策略模式可以避免使用多重条件(if-else)语句。多重条件语句不易维护,它把采取哪一种算法或采取哪一种行为的逻辑与算法或行为的逻辑混合在一起,统统列在一个多重条件语句里面,比使用继承的办法还要原始和落后。
缺点:
1,客户端必须知道所有的策略类,并自行决定使用哪一个策略类。这就意味着客户端必须理解这些算法的区别,以便适时选择恰当的算法类。换言之,策略模式只适用于客户端知道算法或行为的情况。
2,由于策略模式把每个具体的策略实现都单独封装成为类,如果备选的策略很多的话,那么对象的数目就会很可观。
和其他设计模式的区别:
1,与状态模式
在解决的问题上,状态模式是解决内在状态的改变,而策略模式是解决内部算法的改变。在解决的方法上,状态模式是自我控制状态的改变,而策略模式是由外部制定使用使用啥策略。
2,简单工厂模式
简单工厂模式是创建型模式,关注对象的创建。策略模式是行为型模式,关注行为的封装。简单工厂模式是根据不同的条件返回一个适合的类给你使用,然后调用者使用工厂类返回的类去完成相应的操作。而策略模式是必须首先创建一个想使用的类实例,然后实例被当作参数传递进去,既而通过该实例去调用不用的算法。在简单工厂模式中实现了通过条件选取一个类去实例化对象,策略模式则将选取相应对象的工作交给模式的使用者,它本身不去做选取工作。
经典例子:排序策略。排序器负责执行排序,具体按升序还是降序,由策略决定;运行时可以随时切换。 下面用 C++20 的 std::span 和 std::ranges::sort 实现: #include <algorithm> #include <functional> #include <iostream> #include <span> #include <vector> // 策略接口:定义统一的排序行为 class SortStrategy { public: virtual ~SortStrategy() = default; virtual void sort(std::span<int> data) const = 0; }; // 具体策略:升序 class AscendingSort final : public SortStrategy { public: void sort(std::span<int> data) const override { std::ranges::sort(data); } }; // 具体策略:降序 class DescendingSort final : public SortStrategy { public: void sort(std::span<int> data) const override { std::ranges::sort(data, std::greater<>{}); } }; // 上下文:将排序工作委托给当前策略 class Sorter { public: explicit Sorter(const SortStrategy& strategy) : strategy_(&strategy) {} void setStrategy(const SortStrategy& strategy) { strategy_ = &strategy; } void sort(std::span<int> data) const { strategy_->sort(data); } private: // 不拥有策略对象,策略必须在使用期间保持有效 const SortStrategy* strategy_; }; int main() { std::vector<int> data{3, 1, 4, 2}; AscendingSort ascending; DescendingSort descending; Sorter sorter{ascending}; auto print = [&] { for (int value : data) { std::cout << value << ' '; } std::cout << '\n'; }; sorter.sort(data); print(); // 1 2 3 4 sorter.setStrategy(descending); sorter.sort(data); print(); // 4 3 2 1 }编译运行:
g++ -std=c++20 main.cpp -o demo
./demo
三个角色对应如下:
角色 示例 职责
━━━━━━━━━━ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
策略接口 SortStrategy 定义统一调用方式
────────── ───────────────────────────────
具体策略 AscendingSort、DescendingSort 实现不同算法行为
────────── ───────────────────────────────
上下文 Sorter 持有策略并委托执行
核心是“组合 + 委托”:
Sorter 不需要知道排序细节。新增一种策略时,只需实现 SortStrategy,无需修改 Sorter。策略模式
本身不依赖 C++20,这里用 C++20 简化了数据视图和排序调用。