接口隔离原则:面向对象设计之接口隔离原则-CSDN博客
设计模式工厂模式: 设计模式之工厂模式-CSDN博客
迭代器模式:设计模式之迭代器模式-CSDN博客
适配器模式:设计模式之适配器模式-CSDN博客
过滤器模式:设计模式之过滤器模式-CSDN博客
观察者模式:设计模式之观察者模式-CSDN博客
空对象模式:设计模式之空对象模式-CSDN博客
桥接模式:设计模式之桥接模式-CSDN博客
责任链模式:设计模式之责任链模式-CSDN博客
策略模式:设计模式之策略模式-CSDN博客
单例模式:设计模式之单例模式-CSDN博客
目录
1.简介
策略模式属于行为类设计模式;在策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改。
在策略模式定义了一系列算法或策略,并将每个算法封装在独立的类中,使得它们可以互相替换。通过使用策略模式,可以在运行时根据需要选择不同的算法,而不需要修改客户端代码。也是说这些算法所完成的功能类型是一样的,对外接口也是一样的,只是不同的策略为引起环境角色(持有一个策略类的引用,最终给客户端调用)表现出不同的行为。
相比于使用大量的if...else,使用策略模式可以降低复杂度,使得代码更容易维护。在我之前的博客中优化多层if-else-if就用到了策略模式。
2.结构
通用UML类图如下图所示:
策略模式包含以下几个核心角色:
环境(Context):维护一个对策略对象的引用,负责将客户端请求委派给具体的策略对象执行。环境类可以通过依赖注入、简单工厂等方式来获取具体策略对象。
抽象策略(Strategy):定义了策略对象的公共接口或抽象类,规定了具体策略类必须实现的方法。
具体策略(Concrete Strategy):实现了抽象策略定义的接口或抽象类,包含了具体的算法实现。如上述图中ConcreteStategyA、ConcreteStategyB、ConcreteStategyC。
3.实现
3.1.动态策略
以通信行业的数据加密为例,我们平时的语音、文字、图像等数据信息,它们通过无线或有线传输到对端,如果是无线传输就需要编码、加密等手段,那么加密的方式是不是就相当于策略,于是我们可以先定义策略基类:
cpp
class IDataCodec
{
public:
virtual ~IDataCodec() {}
virtual int encodeData(const char* pSrcData, int nSrcLen, char* pDestData, int& nDestLen) = 0;
virtual int decodeData(const char* pSrcData, int nSrcLen, char* pDestData, int& nDestLen) = 0;
};
假如加密手段有CRC、汉明编码、AES等,于是就在IDataCodec的基础上具体实现编解码,代码如下:
cpp
//CRC编码
class CCrcParityCodec : public IDataCodec
{
public:
explicit CCrcParityCodec(bool isMaster);
CCrcParityCodec(const CCrcParityCodec& src){
this->m_bMaster = src.m_bMaster;
}
virtual ~CCrcParityCodec();
public:
int encodeData(const char* pSrcData, int nSrcLen, char* pDestData, int& nDestLen) override{
//...
return 1;
}
int decodeData(const char* pSrcData, int nSrcLen, char* pDestData, int& nDestLen) override {
//...
return 1;
}
private:
bool m_bMaster;
};
//汉明编码
class CHmCodec : public IDataCodec
{
public:
explicit CHmCodec();
virtual ~CHmCodec();
public:
int encodeData(const char* pSrcData, int nSrcLen, char* pDestData, int& nDestLen) override{
//...
return 1;
}
int decodeData(const char* pSrcData, int nSrcLen, char* pDestData, int& nDestLen) override{
//...
return 1;
}
};
//AES
class CAesCodec : public IDataCodec
{
public:
explicit CAesCodec();
virtual ~CAesCodec();
public:
int encodeData(const char* pSrcData, int nSrcLen, char* pDestData, int& nDestLen) override{
//...
return 1;
}
int decodeData(const char* pSrcData, int nSrcLen, char* pDestData, int& nDestLen) override{
//...
return 1;
}
};
//不需要编码的类
class CNoUseDataCodec : public IDataCodec
{
public:
virtual ~CNoUseDataCodec() {}
int encodeData(const char* pSrcData, int nSrcLen, char* pDestData, int& nDestLen) override{
pDestData = const_cast<char*>(pSrcData);
nDestLen = nSrcLen;
return 1;
}
int decodeData(const char* pSrcData, int nSrcLen, char* pDestData, int& nDestLen) override{
pDestData = const_cast<char*>(pSrcData);
nDestLen = nSrcLen;
return 1;
}
};
上下文定义为:
cpp
class CDataContext
{
public:
CDataContext() : m_pDataCodec(&m_noUseDataCodec) {}
void setDataCodec(IDataCodec* pCodec) { m_pDataCodec = pCodec; }
int encodeData(const char* pSrcData, int nSrcLen, char* pDestData, int& nDestLen) {
return m_pDataCodec->encodeData(pSrcData, nSrcLen, pDestData,nDestLen);
}
int decodeData(const char* pSrcData, int nSrcLen, char* pDestData, int& nDestLen) {
return m_pDataCodec->decodeData(pSrcData, nSrcLen, pDestData,nDestLen);
}
private:
IDataCodec* m_pDataCodec;
static CNoUseDataCodec m_noUseDataCodec;
};
CNoUseDataCodec CDataContext::m_noUseDataCodec;
测试例子:
cpp
int main()
{
CDataContext context;
std::unique_ptr<IDataCodec> pCRCCodec(new CCrcParityCodec());
std::unique_ptr<IDataCodec> pCRCCodec(new CHmCodec());
std::unique_ptr<IDataCodec> pCRCCodec(new CAesCodec());
context.setDataCodec(pCRCCodec.get());
context->encodeData(...);
context.setDataCodec(pCRCCodec.get());
context->encodeData(...);
context.setDataCodec(pCRCCodec.get());
context->encodeData(...);
}
3.2.函数式策略
以排序为例:std::sort,它的实现代码如下:
cpp
template <class _RanIt, class _Pr>
_CONSTEXPR20 void sort(const _RanIt _First, const _RanIt _Last, _Pr _Pred) { // order [_First, _Last)
_Adl_verify_range(_First, _Last);
const auto _UFirst = _Get_unwrapped(_First);
const auto _ULast = _Get_unwrapped(_Last);
_Sort_unchecked(_UFirst, _ULast, _ULast - _UFirst, _Pass_fn(_Pred));
}
最后一个参数_Pr就是排序的策略,是按照大小还是按照名称还是其它等等,这些都是具体的排序策略,默认的排序策略就是std::less<>
cpp
template <class _RanIt>
_CONSTEXPR20 void sort(const _RanIt _First, const _RanIt _Last) { // order [_First, _Last)
_STD sort(_First, _Last, less<>{});
}
我们也可以自定义策略,如:
cpp
vector<int> value{34, 3,6,10,7,1};
//默认策略
std::sort(value.begin(), value.end());
//全局函数策略
int compare(int x, int y){ return x > y;}
std::sort(value.begin(), value.end(), compare);
//匿名表达式策略
std::sort(value.begin(), value.end(), [](int x, int y){ return x > y; });
//仿函数策略
struct MyFunctor{
int compare(int x, int y) {return x > y;}
};
std::sort(value.begin(), value.end(), MyFunctor());
4.使用场景
如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。
在处理多种可能的情况时策略模式可以避免使用嵌套的if-else或switch-case语句。通过将每个条件与一个策略类关联,可以动态地选择合适的策略。
如果需要根据不同的条件或参数选择不同的算法,策略模式可以帮助实现这一自标。客户端代码可以与一组策略类交互,并在运行时选择最合适的算法。
5.总结
优点:
灵活性和可扩展性:策略模式提供了一种灵活的机制来改变对象的行为。通过使用不同的策略类,可以在不修改原有代码的基础上,方便地增加新的策略。这有助于保持软件的可扩展性和灵活性。
消除条件语句:策略模式通过消除显式的条件语句(如if-else或switch-case语句),使得代码更加简洁易读。客户端只需要与抽象的策略接口交互,而无需关心具体的实现细节。
更好的组织代码:策略模式有助于将相关的算法和行为组织在一起,形成独立的策略类。这有助于提高代码的组织性和可维护性。
便于替换算法:策略模式使得算法的替换变得更加简单。客户端代码与策略接口的交互保持不变,可以随时替换为不同的实现。这对于测试和调试也很有帮助。
支持开放-封闭原则:策略模式符合开放封闭原则、即软件实体(类、模块、函数等)应该对扩展开放,而对修改封闭。通过将算法封装在独立的策略类中,可以方便地添加新的策略,而无需修改现有代码。
缺点:
增加了类的数量:使用策略模式会增加系统的类数量,这可能会使得系统变得更加复杂。
客户端需要知道所有策略:客户端代码必须知道所有的策略类,并根据真体情况选择合适的策略。这可能会限制客户端的灵活性和可扩展性。
违反最少知识原则:最少知识原则要求一个对象应该尽量少的了解其他对象的信息。但在策略模式中,客户端可能需要了解所有策略类的信息,这可能违反了最少知识原则。
可能会导致设计过度复杂:如果过度使用策略模式,可能会导致设计变得过于复杂和繁锁,增加了维护的难度。