哈喽,各位盆友们!我是你们亲爱的学徒小z,今天给大家分享的文章是设计模式的------策略模式。
定义
- 定义一组算法,将每个算法都封装起来,并且使它们之间可以互换。
- 策略模式的重点就是封装角色,它是借用了代理模式的思路,它和代理模式差别就是策略模式的封装角色和被封装的策略类不用是同一个接口,如果是同一个接口那就成为了代理模式。
通用类图
1.具体组成
-
Context封装角色
持有一个策略类的引用,用于操作策略。它也叫做上下文角色,起承上启下封装作用,屏蔽高层模块对策略、算法的直接访问, 封装可能存在的变化。
-
Strategy抽象策略角色
策略、算法家族的抽象,通常为接口,定义每个策略或算法必须具有的方法和属性。类图中的AlgorithmInterface是什么意思,algorithm是"运算法则"的意思
-
ConcreteStrategy具体策略角色
实现抽象策略中的操作,该类含有具体的算法。
2.具体代码
cpp
//抽象的策略角色
class Strategy {
public:
virtual ~Strategy() {}
virtual void execute() = 0;
};
//具体策略角色
class ConcreteStrategyA : public Strategy {
public:
void execute() override {
std::cout << "执行策略A" << std::endl;
}
};
class ConcreteStrategyB : public Strategy {
public:
void execute() override {
std::cout << "执行策略B" << std::endl;
}
};
//封装角色
class Context {
private:
Strategy* strategy;
public:
Context(Strategy* s) : strategy(s) {}
~Context() {
delete strategy;
}
//设置具体策略
void setStrategy(Strategy* s) {
delete strategy;
strategy = s;
}
//封装后的策略方法
void executeStrategy() {
strategy->execute();
}
};
int main() {
Context context(new ConcreteStrategyA());
context.executeStrategy(); // 输出:执行策略A
context.setStrategy(new ConcreteStrategyB());
context.executeStrategy(); // 输出:执行策略B
return 0;
}
3.优点
- 算法可以自由切换
- 扩展性良好
- 避免使用多重判断条件
4.缺点
- 策略类数量多
- 所有策略类都需要对外暴露
使用场景
-
多个类只有在算法或行为上稍有不同的场景。
-
算法需要自由切换的场景
-
需要屏蔽算法规则的场景
比如:一个算法你只要知道一个名字就可以了,传递相关的数字进来,反馈一个运算结果
策略模式的注意事项
如果系统中的一个策略家族的具体策略数量超过4个,则需要考虑使用混合模式,解决策略类膨胀和对外暴露的问题,否则日后维护困难
策略枚举
策略枚举是一种使用枚举(enum
)来封装策略模式的实践
策略枚举通常包括以下部分:
- 枚举类型:定义了一个枚举类型,其中每个枚举值代表一种策略。
- 策略函数:为每种策略定义一个函数,该函数实现具体的策略行为。
- 策略选择:根据枚举值来调用相应的策略函数。
优势:
markdown
1. 易于扩展:添加新的策略只需要增加新的枚举值和对应的策略函数。
2. 类型安全:枚举类提供了类型安全,避免了不同枚举值之间的混淆。
我们先来看一个java的策略枚举实例。
java
//策略枚举
public enum Calculator {
//加法运算
ADD("+"){
public int exec(int a,int b){
return a+b;
}
},
//减法运算
SUB("-"){
public int exec(int a,int b){
return a - b;
}
};
String value = "";
//定义成员值类型
private Calculator(String _value){
this.value = _value;
}
//获得枚举成员的值
public String getValue(){
return this.value;
}
//声明一个抽象函数
public abstract int exec(int a,int b);
}
//场景类
public class Client {
public static void main(String[] args) {
//输入的两个参数是数字
int a = Integer.parseInt(args[0]);
String symbol = args[1]; //符号
int b = Integer.parseInt(args[2]);
System.out.println("输入的参数为:"+Arrays.toString(args));
System.out.println("运行结果为:"+a+symbol+b+"="+Calculator.ADD.exec(a,b));
}
}
下面是一个c++例子。c++不支持枚举值实现方法重写,因此可以为每个枚举值定义相应的枚举函数来完成
cpp
enum class SortStrategy {
QuickSort,
InsertionSort
};
//策略函数
void quickSort(std::vector<int>& data) {
std::sort(data.begin(), data.end()); // 使用标准库中的快速排序实现
}
void insertionSort(std::vector<int>& data) {
// 插入排序的实现
for (size_t i = 1; i < data.size(); ++i) {
int key = data[i];
int j = i - 1;
while (j >= 0 && data[j] > key) {
data[j + 1] = data[j];
--j;
}
data[j + 1] = key;
}
}
//策略枚举
void sort(std::vector<int>& data, SortStrategy strategy) {
switch (strategy) {
case SortStrategy::QuickSort:
quickSort(data);
break;
case SortStrategy::InsertionSort:
insertionSort(data);
break;
// 可以继续添加其他排序策略
}
}
int main() {
std::vector<int> data = {5, 3, 8, 4, 2};
// 使用快速排序策略
sort(data, SortStrategy::QuickSort);
for (int value : data) {
std::cout << value << " ";
}
std::cout << std::endl;
// 使用插入排序策略
data = {5, 3, 8, 4, 2}; // 重置数据
sort(data, SortStrategy::InsertionSort);
for (int value : data) {
std::cout << value << " ";
}
std::cout << std::endl;
return 0;
}