设计模式之策略模式

面向对象设计原则

接口隔离原则面向对象设计之接口隔离原则-CSDN博客
设计模式

工厂模式设计模式之工厂模式-CSDN博客

迭代器模式设计模式之迭代器模式-CSDN博客

适配器模式设计模式之适配器模式-CSDN博客

过滤器模式设计模式之过滤器模式-CSDN博客

观察者模式设计模式之观察者模式-CSDN博客

空对象模式设计模式之空对象模式-CSDN博客

桥接模式设计模式之桥接模式-CSDN博客

责任链模式设计模式之责任链模式-CSDN博客

策略模式设计模式之策略模式-CSDN博客

单例模式设计模式之单例模式-CSDN博客

目录

1.简介

2.结构

3.实现

3.1.动态策略

3.2.函数式策略

4.使用场景

5.总结


1.简介

策略模式属于行为类设计模式;在策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改。

在策略模式定义了一系列算法或策略,并将每个算法封装在独立的类中,使得它们可以互相替换。通过使用策略模式,可以在运行时根据需要选择不同的算法,而不需要修改客户端代码。也是说这些算法所完成的功能类型是一样的,对外接口也是一样的,只是不同的策略为引起环境角色(持有一个策略类的引用,最终给客户端调用)表现出不同的行为

相比于使用大量的if...else,使用策略模式可以降低复杂度,使得代码更容易维护。在我之前的博客中优化多层if-else-if就用到了策略模式。

C++之多层 if-else-if 结构优化(二)_c++ 怎么优化很多if 的代码呢-CSDN博客

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语句),使得代码更加简洁易读。客户端只需要与抽象的策略接口交互,而无需关心具体的实现细节。
更好的组织代码:策略模式有助于将相关的算法和行为组织在一起,形成独立的策略类。这有助于提高代码的组织性和可维护性。
便于替换算法:策略模式使得算法的替换变得更加简单。客户端代码与策略接口的交互保持不变,可以随时替换为不同的实现。这对于测试和调试也很有帮助。
支持开放-封闭原则:策略模式符合开放封闭原则、即软件实体(类、模块、函数等)应该对扩展开放,而对修改封闭。通过将算法封装在独立的策略类中,可以方便地添加新的策略,而无需修改现有代码。

缺点:

增加了类的数量:使用策略模式会增加系统的类数量,这可能会使得系统变得更加复杂。
客户端需要知道所有策略:客户端代码必须知道所有的策略类,并根据真体情况选择合适的策略。这可能会限制客户端的灵活性和可扩展性。
违反最少知识原则:最少知识原则要求一个对象应该尽量少的了解其他对象的信息。但在策略模式中,客户端可能需要了解所有策略类的信息,这可能违反了最少知识原则。
可能会导致设计过度复杂:如果过度使用策略模式,可能会导致设计变得过于复杂和繁锁,增加了维护的难度。

相关推荐
哪 吒3 小时前
最简单的设计模式,抽象工厂模式,是否属于过度设计?
设计模式·抽象工厂模式
Theodore_10223 小时前
4 设计模式原则之接口隔离原则
java·开发语言·设计模式·java-ee·接口隔离原则·javaee
‘’林花谢了春红‘’4 小时前
C++ list (链表)容器
c++·链表·list
转世成为计算机大神6 小时前
易考八股文之Java中的设计模式?
java·开发语言·设计模式
机器视觉知识推荐、就业指导6 小时前
C++设计模式:建造者模式(Builder) 房屋建造案例
c++
小乖兽技术7 小时前
23种设计模式速记法
设计模式
Yang.998 小时前
基于Windows系统用C++做一个点名工具
c++·windows·sql·visual studio code·sqlite3
熬夜学编程的小王8 小时前
【初阶数据结构篇】双向链表的实现(赋源码)
数据结构·c++·链表·双向链表
zz40_8 小时前
C++自己写类 和 运算符重载函数
c++