C++软件设计模式之策略模式
策略模式的动机、意图和适用场合
1. 动机
在软件开发中,经常会遇到需要在运行时选择不同算法或行为的情况。传统的做法是使用条件语句(如 if-else
或 switch
)来选择不同的实现。然而,这种方式有几个缺点:
- 代码冗长:每个条件分支都需要编写重复的代码。
- 难以维护:当需要添加新的算法或行为时,需要修改现有的条件语句,容易引入错误。
- 耦合度高:客户端代码与具体的算法实现紧密耦合,难以扩展和复用。
策略模式通过将算法或行为封装成独立的类(策略类),并通过一个抽象类或接口来定义这些策略类的共同行为,解决了上述问题。
2. 意图
策略模式的意图是定义一系列算法,将它们封装起来,并使它们可以相互替换。策略模式使得算法可以独立于使用它的客户端而变化。
3. 适用场合
策略模式适用于以下几种情况:
- 多个相关的类仅有行为不同:策略模式可以将这些类的行为提取出来,作为不同的策略类,从而避免类的重复代码。
- 需要使用一个算法的不同变体:客户端可以在运行时根据需要选择不同的算法实现。
- 算法需要选择或切换:策略模式允许在运行时动态地选择和切换不同的算法。
- 避免条件语句:通过策略模式,可以减少或消除复杂的条件语句,使代码更加清晰和易于维护。
为什么策略模式是行为型模式而不是结构型模式
行为型模式的定义
行为型模式关注对象之间的职责分配和交互。它们主要解决对象如何通信和协作的问题,而不是对象的创建或结构。
策略模式的特点
- 关注对象的行为:策略模式的核心是算法或行为的封装和替换,而不是对象的创建或结构。
- 动态选择和切换:策略模式允许在运行时动态地选择和切换不同的算法,这依赖于对象之间的交互。
- 解耦客户端和算法:客户端代码通过接口与策略类交互,而不是直接调用具体的算法实现,这使得客户端代码与具体算法实现解耦。
- 增强灵活性和可扩展性:通过将算法封装成独立的类,可以轻松地添加、删除或修改算法,而不会影响客户端代码。
区别于结构型模式
- 结构型模式关注对象的组合:结构型模式主要解决如何组合类或对象以形成更大的结构,例如适配器模式、装饰者模式和组合模式等。它们关注的是类或对象的结构和关系。
- 行为型模式关注对象的交互:策略模式关注的是对象之间的行为和交互,通过定义不同的策略类,使客户端可以在运行时选择和切换不同的算法。
示例
以下是一个简单的C++示例,展示了如何使用策略模式来封装不同的排序算法。
// 抽象策略类
class SortingStrategy {
public:
virtual void sort(int* array, int length) = 0;
virtual ~SortingStrategy() {}
};
// 具体策略类:冒泡排序
class BubbleSort : public SortingStrategy {
public:
void sort(int* array, int length) override {
for (int i = 0; i < length - 1; ++i) {
for (int j = 0; j < length - i - 1; ++j) {
if (array[j] > array[j + 1]) {
std::swap(array[j], array[j + 1]);
}
}
}
}
};
// 具体策略类:快速排序
class QuickSort : public SortingStrategy {
public:
void sort(int* array, int length) override {
quickSort(array, 0, length - 1);
}
private:
void quickSort(int* array, int low, int high) {
if (low < high) {
int pi = partition(array, low, high);
quickSort(array, low, pi - 1);
quickSort(array, pi + 1, high);
}
}
int partition(int* array, int low, int high) {
int pivot = array[high];
int i = (low - 1);
for (int j = low; j < high; j++) {
if (array[j] <= pivot) {
i++;
std::swap(array[i], array[j]);
}
}
std::swap(array[i + 1], array[high]);
return (i + 1);
}
};
// 上下文类
class Context {
private:
SortingStrategy* strategy;
public:
Context(SortingStrategy* strategy) : strategy(strategy) {}
void setStrategy(SortingStrategy* strategy) {
this->strategy = strategy;
}
void sortArray(int* array, int length) {
strategy->sort(array, length);
}
~Context() {
delete strategy;
}
};
// 客户端代码
int main() {
int array[5] = {5, 2, 8, 1, 9};
Context* context = new Context(new BubbleSort());
context->sortArray(array, 5);
for (int i = 0; i < 5; ++i) {
std::cout << array[i] << " ";
}
std::cout << std::endl;
context->setStrategy(new QuickSort());
context->sortArray(array, 5);
for (int i = 0; i < 5; ++i) {
std::cout << array[i] << " ";
}
std::cout << std::endl;
delete context;
return 0;
}
总结
策略模式是一种行为型设计模式,它通过将算法或行为封装成独立的类,使客户端可以在运行时动态地选择和切换不同的算法。策略模式关注对象的行为和交互,而不是对象的创建或结构,因此它被归类为行为型模式。通过使用策略模式,可以提高代码的灵活性和可维护性,减少条件语句的使用,使代码更加清晰和易于扩展。
使用策略模式,封装不同的信号滤波处理算法的C++代码完整示例
面是一个使用策略模式封装不同信号滤波处理算法的完整C++示例。这个示例中,我们将实现两种不同的滤波策略:简单的平均滤波和中值滤波。
1. 抽象策略类
首先,定义一个抽象的滤波策略类 FilterStrategy
,它包含一个纯虚函数 filter
,用于定义滤波算法的共同接口。
#include <iostream>
#include <vector>
#include <algorithm>
// 抽象策略类
class FilterStrategy {
public:
virtual std::vector<double> filter(const std::vector<double>& signal) const = 0;
virtual ~FilterStrategy() = default;
};
2. 具体策略类
接下来,实现两个具体的滤波策略类:MeanFilter
和 MedianFilter
。
2.1 平均滤波策略
class MeanFilter : public FilterStrategy {
public:
std::vector<double> filter(const std::vector<double>& signal) const override {
int windowSize = 3; // 滤波窗口大小
int n = signal.size();
std::vector<double> filteredSignal(n, 0.0);
for (int i = 0; i < n; ++i) {
double sum = 0.0;
int count = 0;
for (int j = -windowSize / 2; j <= windowSize / 2; ++j) {
if (i + j >= 0 && i + j < n) {
sum += signal[i + j];
count++;
}
}
filteredSignal[i] = sum / count;
}
return filteredSignal;
}
};
2.2 中值滤波策略
class MedianFilter : public FilterStrategy {
public:
std::vector<double> filter(const std::vector<double>& signal) const override {
int windowSize = 3; // 滤波窗口大小
int n = signal.size();
std::vector<double> filteredSignal(n, 0.0);
for (int i = 0; i < n; ++i) {
std::vector<double> window;
for (int j = -windowSize / 2; j <= windowSize / 2; ++j) {
if (i + j >= 0 && i + j < n) {
window.push_back(signal[i + j]);
}
}
std::sort(window.begin(), window.end());
filteredSignal[i] = window[window.size() / 2];
}
return filteredSignal;
}
};
3. 上下文类
定义一个上下文类 SignalProcessor
,它使用一个 FilterStrategy
对象来处理信号。
class SignalProcessor {
private:
FilterStrategy* strategy;
public:
SignalProcessor(FilterStrategy* strategy) : strategy(strategy) {}
void setStrategy(FilterStrategy* strategy) {
this->strategy = strategy;
}
std::vector<double> process(const std::vector<double>& signal) {
return strategy->filter(signal);
}
~SignalProcessor() {
delete strategy;
}
};
4. 客户端代码
最后,编写客户端代码来演示如何使用不同的滤波策略。
int main() {
// 创建一个信号
std::vector<double> signal = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0};
// 创建滤波策略对象
FilterStrategy* meanFilter = new MeanFilter();
FilterStrategy* medianFilter = new MedianFilter();
// 创建信号处理器对象,并设置初始滤波策略
SignalProcessor processor(meanFilter);
// 使用平均滤波处理信号
std::vector<double> filteredSignal = processor.process(signal);
std::cout << "Mean Filter: ";
for (double value : filteredSignal) {
std::cout << value << " ";
}
std::cout << std::endl;
// 切换到中值滤波策略
processor.setStrategy(medianFilter);
// 使用中值滤波处理信号
filteredSignal = processor.process(signal);
std::cout << "Median Filter: ";
for (double value : filteredSignal) {
std::cout << value << " ";
}
std::cout << std::endl;
// 释放资源
delete meanFilter;
delete medianFilter;
return 0;
}
总结
在这个示例中,我们使用策略模式封装了两种不同的信号滤波算法:平均滤波和中值滤波。通过定义抽象的 FilterStrategy
类和具体的 MeanFilter
及 MedianFilter
类,我们实现了算法的封装和替换。SignalProcessor
类作为上下文类,使用策略对象来处理信号,并可以在运行时动态地切换不同的滤波策略。这种方式提高了代码的灵活性和可维护性。