C++設計模式之Strategy策略模式
- 模式定義
- 核心思想
- 動機(Motivation)
- 結構(Structure)
- 實現步驟
- 1. 定義策略接口(基于繼承)
- 2.實現具體策略
- 3.上下文類(Context)
- 4. 在main中調用
- 應用場景(基于繼承)
- 1.定義策略接口
- 2.實現具體策略
- 3.上下文類(Context)
- 4.在main中調用測試
- 現代 C++ 優化(基于 std::function)
- 1.使用函數對象替代接口
- 2.定義策略函數
- 3.main.cpp中使用示例
- 優化方向
- 1.編譯時策略(模板實現)
- 2.線程安全策略切換
- 要點總結
模式定義
策略模式:定義一系列算法,把她們一個個封裝起來,并且使它們可以相互替換(變化)。該模式使得算法可以獨立于使用它的客戶程序(穩定)而變化(擴展,子類化)。
需要區分策略模式和之前提到的模板方法模式的不同,模板方法是通過繼承來改變部分步驟,而策略模式是通過組合不同的策略對象來改變整體行為。
核心思想
- 解耦算法與客戶端:將算法邏輯從主類中分離,避免代碼臃腫。
- 運行時動態切換:通過更換策略對象,靈活改變程序行為。
- 開閉原則:新增算法無需修改現有代碼,只需添加新策略類。
動機(Motivation)
- 在軟件構建過程中,某些對象使用的算法可能多種多樣,經常改變,如果將這些算法都編碼到對象中,將會使對象變得異常復雜;而且有時候支持不使用的算法也是一個性能負擔。
- 如何在運行時根據需要透明地更改對象的算法?將算法與對象本身解耦,從而避免上述問題?
結構(Structure)
實現步驟
1. 定義策略接口(基于繼承)
startegy.h
#pragma once
class Strategy {//策略模式的運算法則
public:virtual ~Strategy() {}virtual void doSomething() = 0;
};
2.實現具體策略
concretestrategy.h
#pragma once
#include<iostream>
#include"strategy.h"
class ConcreteStrategy1 :public Strategy {
public:void doSomething()override {std::cout << "ConcreteStrategy1 doSomething" << std::endl;}
};class ConcreteStrategy2 :public Strategy {
public:void doSomething()override {std::cout << "ConcreteStrategy2 doSomething" << std::endl;}
};
3.上下文類(Context)
持有策略對象的引用,并委托算法執行
context.h
#pragma once
#include"strategy.h"
class Context {
public://構造函數設置具體策略Context(Strategy *strategy) {strategy_ = strategy;}//封裝后的策略方法void doAnything() {strategy_->doSomething();}private:Strategy *strategy_ = nullptr;
};
4. 在main中調用
#include "concretestrategy.h"
#include "context.h"int main()
{Strategy *strategy = new ConcreteStrategy1();//聲明上下文對象Context context(strategy);context.doAnything();delete strategy;
}
應用場景(基于繼承)
傳統實現(基于繼承),實現快速排序算法,歸并排序算法的策略模式
1.定義策略接口
sortingstrategy.h
#pragma once
#include<iostream>
#include<vector>//策略接口:定義算法的抽象操作
class SortingStrategy {
public:virtual ~SortingStrategy() = default;virtual void sort(std::vector<int>&data)const = 0;
};
2.實現具體策略
#pragma once
#include"sortingstrategy.h"
//具體策略1:快速排序
class QuickSort :public SortingStrategy
{
public:void sort(std::vector<int>&data)const override {std::cout << "Sorting with QuickSort\n";//快速排序具體實現}
};class MergeSort :public SortingStrategy
{
public:void sort(std::vector<int>&data)const override {std::cout << "Sorting with MergeSort\n";//歸并排序具體實現}
};
3.上下文類(Context)
持有策略對象的引用,并委托算法執行
context.h
#pragma once
#include"concretesorting.h"
class Sorter {
private:std::unique_ptr<SortingStrategy> strategy_; //使用智能指針管理策略對象public:explicit Sorter(std::unique_ptr<SortingStrategy> strategy):strategy_(std::move(strategy)){}//動態切換策略void setStrategy(std::unique_ptr<SortingStrategy>strategy) {strategy_ = std::move(strategy);}//執行排序void executeSort(std::vector<int>&data)const {if (strategy_) {strategy_->sort(data);}}
};
4.在main中調用測試
#include"context.h"
#include<vector>int main()
{std::vector<int> data = { 5,2,7,1,9 };Sorter sorter(std::make_unique<QuickSort>());sorter.executeSort(data); //輸出:Sorting with QuickSortsorter.setStrategy(std::make_unique<MergeSort>());sorter.executeSort(data); //輸出Sorting with MergeSortreturn 0;
}
現代 C++ 優化(基于 std::function)
1.使用函數對象替代接口
無需繼承策略接口,直接通過std::function封裝算法:
context.h
#pragma once
#include<functional>
#include<vector>class Sorter {
public:using Strategy = std::function<void(std::vector<int>&)>;explicit Sorter(Strategy strategy):strategy_(std::move(strategy)){}void setStrategy(Strategy strategy) {strategy_ = std::move(strategy);}void executeSort(std::vector<int>&data)const {if (strategy_) {strategy_(data);}}private:Strategy strategy_;
};
2.定義策略函數
context.h
void quickSort(std::vector<int>& data) {std::cout << "Sorting with QuickSort\n";// 快速排序實現...
}void mergeSort(std::vector<int>& data) {std::cout << "Sorting with MergeSort\n";// 歸并排序實現...
}// 支持 Lambda 表達式
auto bubbleSort = [](std::vector<int>& data) {std::cout << "Sorting with BubbleSort\n";// 冒泡排序實現...
};
3.main.cpp中使用示例
#include"context.h"int main()
{std::vector<int> data = { 5,2,7,1,9 };Sorter sorter(quickSort);sorter.executeSort(data); //輸出Sorting with QuickSortsorter.setStrategy(mergeSort); //輸出Sorting with MergeSortsorter.executeSort(data);sorter.setStrategy(bubbleSort);//輸出Sorting with BubbleSortsorter.executeSort(data);//直接傳達Lambdasorter.setStrategy([](std::vector<int>&data) {std::cout << "Custom Lambda Strategy \n";});sorter.executeSort(data);return 0;
}
優化方向
1.編譯時策略(模板實現)
通過模板參數在編譯時綁定 策略,消除運行時開銷:
#pragma once
#include<vector>
#include<iostream>template<typename Strategy>
class ComplieTimeSorter {
public:void executeSort(std::vector<int>&data)const {Strategy::sort(data); //策略需要提供靜態方法}
};//策略類無需繼承接口
struct QuickSort {static void sort(std::vector<int>&data) {std::cout << "Compile-Time QuickSort \n";}
};int main()
{std::vector<int> data = { 5,2,7,1,9 };ComplieTimeSorter<QuickSort>sorter;sorter.executeSort(data); //輸出Complie-Time QuickSortsystem("pause");return 0;
}
2.線程安全策略切換
在多線程環境中安全且策略:
#pragma once
#include<mutex>
#include<vector>class ThreadSafeSorter {
public:using Strategy = std::function<void(std::vector<int>&)>;void setStrategy(Strategy strategy) {std::lock_guard<std::mutex>lock(mtx_);strategy_ = std::move(strategy);}void executeSort(std::vector<int>&data)const {std::lock_guard<std::mutex>lock(mtx_);if (strategy_) {strategy_(data);}}
private:mutable std::mutex mtx_;Strategy strategy_;
};
要點總結
- Strategy及其子類為組件提供了一系列可重用的算法,從而可以使得類型在運行時方便地根據需要在各個算法之間進行切換。
- Strategy模式提供了用條件判斷語句以外的另一種選擇,消除條件判斷語句,就是在解耦合。含有許多條件判斷語句的代碼通常都需要Strategy模式。
- 如果Strategy對象沒有實例變量,那么各個上下文可以共享同一個Strategy對象,從而節省對象開銷。