【设计模式】策略模式及函数式编程的替代

本文介绍策略模式以及使用函数式编程替代简单的策略模式。

策略模式

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

在策略模式定义了一系列算法或策略,并将每个算法封装在独立的类中,使得它们可以互相替换。通过使用策略模式,可以在运行时根据需要选择不同的算法,而不需要修改客户端代码。

在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context

对象的执行算法。

上面这段话来自菜鸟教程。简单来说,就是一种封装。Context需要执行某种算法,但是Context无法构造出算法的细节,于是Context需要找Concrete Strategy类获取,不同的Concrete Strategy类对应不同的算法,因为他们继承自一个公共的Abstract Strategy。

作为行为型模式的一种,这个还是一个比较好理解的设计模式,至少对我来说。我大概看了介绍就写了写C++的实现:

首先定义一个公共的父类:

cpp 复制代码
struct AbsBaseStrategy
{
public:
    virtual void show() = 0;
};

然后写两个子类继承:

cpp 复制代码
struct SpecificStrategy1 : AbsBaseStrategy
{
public:
    void show() override
    {
        cout << "SpecificStrategy1 show" << endl;
    }
};

struct SpecificStrategy2 : AbsBaseStrategy
{
public:
    void show() override
    {
        cout << "SpecificStrategy2 show" << endl;
    }
};

写一个公共的上下文,调用策略类的方法。我比较懒,这里就用智能指针了。引用的写法是类似的:

cpp 复制代码
struct TradStrategyCtxt
{
public:
    TradStrategyCtxt(shared_ptr<AbsBaseStrategy> strategy) : m_strategy(strategy){};
    void ctxtShow()
    {
        m_strategy->show();
    }

private:
    shared_ptr<AbsBaseStrategy> m_strategy;
};

然后使用的时候直接传递具体的对象进来就行:

cpp 复制代码
int main()
{
    TradStrategyCtxt ctxt1(make_shared<SpecificStrategy1>());
    TradStrategyCtxt ctxt2(make_shared<SpecificStrategy2>());
    ctxt1.ctxtShow();
    ctxt2.ctxtShow();
}

函数式编程下的策略模式

事实上如果只是调用某一个(或者少量某几个)具体的方法,我们大可不必非得传一个类进来。不同的策略类唯一的意义就是对接口函数进行包装,那如果直接把函数指针传入,也是可以的。还是上面的例子。

我们使用两个函数,代替两个具体的策略类:

cpp 复制代码
void show1()
{
    cout << "SpecificStrategy1 show" << endl;
};

void show2()
{
    cout << "SpecificStrategy2 show" << endl;
};

在Context中,直接传入函数指针:

cpp 复制代码
struct TradStrategyCtxt
{
public:
    TradStrategyCtxt(function<void()> func) : m_func(func){};
    void ctxtShow()
    {
        m_func();
    }

private:
    function<void()> m_func;
};

调用的地方也不需要修改什么,最后的效果是相同的:

cpp 复制代码
int main()
{
    TradStrategyCtxt ctxt1(show1);
    TradStrategyCtxt ctxt2(show2);
    ctxt1.ctxtShow();
    ctxt2.ctxtShow();
}
相关推荐
草莓熊Lotso29 分钟前
Linux 文件描述符与重定向实战:从原理到 minishell 实现
android·linux·运维·服务器·数据库·c++·人工智能
傻乐u兔30 分钟前
C语言进阶————指针4
c语言·开发语言
大模型玩家七七33 分钟前
基于语义切分 vs 基于结构切分的实际差异
java·开发语言·数据库·安全·batch
历程里程碑33 分钟前
Linux22 文件系统
linux·运维·c语言·开发语言·数据结构·c++·算法
牛奔2 小时前
Go 如何避免频繁抢占?
开发语言·后端·golang
寻星探路6 小时前
【深度长文】万字攻克网络原理:从 HTTP 报文解构到 HTTPS 终极加密逻辑
java·开发语言·网络·python·http·ai·https
lly2024067 小时前
Bootstrap 警告框
开发语言
2601_949146538 小时前
C语言语音通知接口接入教程:如何使用C语言直接调用语音预警API
c语言·开发语言
曹牧8 小时前
Spring Boot:如何测试Java Controller中的POST请求?
java·开发语言
在路上看风景8 小时前
19. 成员初始化列表和初始化对象
c++