C++20之设计模式(22):策略模式

策略模式

      • 策略模式
        • 动态策略
        • 静态策略
        • 总结

策略模式

假设您决定使用包含多个字符串的数组或向量,并将它们作为列表输出 ["just", "like", "this"]

如果考虑不同的输出格式,您可能知道需要获取每个元素,并将其与一些额外的标记一起输出。但对于HTML或LaTeX这样的语言,列表还需要开始和结束标记或标记。这两种格式中的任何一种对列表的处理都是相似的(需要输出每个条目)和不同的(条目的输出方式)。这些都可以用一个单独的策略来处理。

我们可以制定一个渲染列表的策略:

  • 渲染开始标签或元素
  • 渲染每一个list元素
  • 渲染关闭标签或元素

可以针对不同的输出格式制定不同的策略,然后将这些策略输入一个通用的、不变的算法来生成文本。这是另一种存在于动态(运行时可替换)和静态(由模板组成的、固定的)类型中的模式。让我们一起来探讨下它们。

动态策略

因此,我们的目标是打印一个markdown或html格式的简单的文本项列表:

enum class OutputFormat { markdown, html };

我们可以在基类ListStrategy中定义打印策略框架:

struct ListStrategy {virtual void start(ostringstream& oss){};virtual void end(ostringstream& oss){};virtual void add_list_item(ostringstream& oss, const string& item){};
};

现在让我们跳到文本处理组件。这个组件将调用打印列表的成员函数, append_list()

struct TextProcessor {void append_list(const verctor<string> items) {listStrategy->start(oss);for (auto &&item : items)listStrategy->add_list_item(oss, item)listStrategy->end(oss);}private:ostringstream oss;unique_ptr<ListStrategy> listStrategy;
};

TextProcessor中我们定义了一个oss的输出字符串缓冲区,我们正在使用的策略是渲染列表,当然还有append_list(),它指定了为了实际渲染一个给定的策略列表,需要采取的一系列步骤。需要注意的是,正如前面所使用的,组合是两种可能的选项之一,可用于实现框架算法的具体实现。相反,我们可以添加像add_list_item()这样的函数作为虚成员,由派生类覆盖:这就是模板方法模式。

现在我们可以继续为列表实现不同的打印策略,比如HtmlListStrategy

struct HtmlListStrategy : ListStrategy {void start(ostringstream& oss) override { oss << "<ul>\n"; }void end(ostringstream& oss) override { oss << "</ul>\n"; }void add_list_item(ostringstream& oss, const string& item) override {oss << "<li>" << item << "</li> \n"}
};

我们可以用类似的方式实现MarkdownListStrategy,但是因为Markdown不需要开始/结束标记,所以我们将只覆盖add_list_item()函数。

struct MarkdownListStrategy : ListStrategy {void add_list_item(ostringstream& oss) override { oss << "*" << item; }
};

现在我们可以开始使用文本处理器,为其提供不同的策略并得到不同的结果。例如:


TextProcessor tp;
tp.set_output_format(OutputFormat::markdown);
tp.append_list({"foo", "bar", "baz"});
cout << tp.str() << endl;// Output:
// * foo
// * bar
// * baz

我们可以为在运行时可切换的策略做准备,这正是我们称之为动态策略的原因。这是在set_output_format()函数中完成的,它的实现很简单

void set_output_format(OutputFormat format) {switch (format) {case OutputFormat::markdown:list_strategy = make_unique<MarkdownListStrategy>();case OutputFormat::html:list_strategy = make_unique<HtmlListStrategy>();break;}
}

现在,从一种策略切换到另一种策略是很简单的,你可以直接看到结果:

tp.clear(); // clears the buffer
tp.set_output_format(OutputFormat::Html);
tp.append_list({"foo", "bar", "baz"});
cout << tp.str() << endl;
// Output:
// <ul>
// <li>foo</li>
// <li>bar</li>
// <li>baz</li>
// </ul>
静态策略

多亏了模板的魔力,你可以将任何策略直接融入到类型中。只需对 TextStrategy 类进行最少的更改:

template <typename LS>
struct TextProcessor {void append_list(const vector<string> items) {list_strategy.start(oss);for (auto& item : items) list_strategy.add_list_item(oss, item);list_strategy.end(oss);}// other functions unchangedprivate:ostringstream oss;LS list_strategy;
};

我们只是添加了 LS 模板参数,使用这种类型创建了一个成员策略,并开始使用它而不是我们之前使用的指针。 append_list() 的结果是相同的:

// markdown
TextProcessor<MarkdownListStrategy> tpm;
tpm.append_list({"foo", "bar", "baz"});
cout << tpm.str() << endl;
// html
TextProcessor<HtmlListStrategy> tph;
tph.append_list({"foo", "bar", "baz"});
cout << tph.str() << endl;

前面示例的输出与动态策略的输出相同。请注意,我们必须创建两个 textProcessor 实例,每个实例都有不同的列表处理策略。

总结

策略设计模式允许你定义算法的框架,然后使用组合来提供与特定策略相关的缺失实现细节。这种方法存在于两种实现方式:

  • 动态策略 只是保持一个指向正在使用的策略的指针/引用。想换一种不同的策略吗?只需更改引用。简单!

  • 静态策略 要求您在编译时选择策略并坚持下去 - 以后没有改变主意的余地

应该使用动态策略还是静态策略?好吧,动态对象允许你在构造对象后重新配置对象。想象一个控制文本输出形式的 UI 设置:你更愿意拥有一个可切换的 TextProcessor 还是两个 TextProcessor<MarkdownStrategy>TextProcessor<HtmlStrategy> 类型的变量?这真的取决于你。

最后一点,你可以限制类型采用的策略集:而不是允许通用 ListStrategy 参数,可以采用 std::variant 列出允许传入的类型。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/50385.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据传输安全--SSL VPN

目录 IPSEC在Client to LAN场景下比较吃力的表现 SSL VPV SSL VPN优势 SSL协议 SSL所在层次 SSL工作原理 SSL握手协议、SSL密码变化协议、SSL警告协议三个协议作用 工作过程 1、进行TCP三次握手、建立网络连接会话 2、客户端先发送Client HELLO包&#xff0c;下图是包…

Oracle对比两表数据的不一致

MINUS 基本语法如下 [SQL 语句 1] MINUS [SQL 语句 2];举个例子&#xff1a; select 1 from dual minus select 2 from dual--运行结果 1-------------------------------- select 2 from dual minus select 1 from dual--运行结果 2所以&#xff0c;如果想找所有不一致的&a…

MySQL with recursive 用法浅析

目录 写在前面 语句功能 with recursive 语法讲解 细节补充 “union all”语句 添加递归终止条件 写在前面 介绍“with recursive”用法的文章不少&#xff0c;但我都觉得讲的不够通俗&#xff0c;所以干脆自己写一篇。话不多说&#xff0c;进入正题。 语句功能 with r…

【数据结构】二叉树链式结构——感受递归的暴力美学

前言&#xff1a; 在上篇文章【数据结构】二叉树——顺序结构——堆及其实现中&#xff0c;实现了二叉树的顺序结构&#xff0c;使用堆来实现了二叉树这样一个数据结构&#xff1b;现在就来实现而二叉树的链式结构。 一、链式结构 链式结构&#xff0c;使用链表来表示一颗二叉树…

python kivy制作GUI界面---ScreenManager多界面跳转

目录 介绍项目演示代码示例介绍 由于开了新的项目,需要制作适合一次开发多端多环境运行的桌面端项目,找了大部分的项目,目前主推的框架主要有:kivy、wxpython、electron等可以快速开发的,由于项目预测体积不会太大,所以暂时不考虑PyQt啦,多了不少,少了不唠,直接进入主…

FPGA:有限状态机

从以下6个实验理解状态机的概念 开发板频率为 50 M H z 50MHz 50MHz&#xff0c;一个时钟周期是 20 n s 20ns 20ns。 1、实验一:LED灯亮0.25秒、灭0.75秒的状态循环 通过之前的分析&#xff0c;我们实现频闪灯时&#xff0c;是让led灯在0.5秒实现一次翻转&#xff0c;而这里…

【我的OpenGL学习进阶之旅】讲一讲GL_TEXTURE_2D和GL_TEXTURE_EXTERNAL_OES的区别

在使用OpenGL ES进行图形图像开发时,我们常使用GL_TEXTURE_2D纹理类型,它提供了对标准2D图像的处理能力。这种纹理类型适用于大多数场景,可以用于展示静态贴图、渲染2D图形和进行图像处理等操作。 另外,有时我们需要从Camera或外部视频源读取数据帧并进行处理。这时,我们…

经典文献阅读之--World Models for Autonomous Driving(自动驾驶的世界模型:综述)

Tip: 如果你在进行深度学习、自动驾驶、模型推理、微调或AI绘画出图等任务&#xff0c;并且需要GPU资源&#xff0c;可以考虑使用UCloud云计算旗下的Compshare的GPU算力云平台。他们提供高性价比的4090 GPU&#xff0c;按时收费每卡2.6元&#xff0c;月卡只需要1.7元每小时&…

linux-conda环境安装配置教程

以下是一个超详细的Linux-Conda环境安装教程,该教程涵盖了从系统准备到环境配置的全过程: 一、准备工作 确认系统环境: 确认操作系统的版本和系统架构。可以通过以下命令获取这些信息:uname -a lsb_release -a这些命令将显示你的内核版本、系统名称、硬件架构和操作系统版…

html+css 实现水波纹按钮

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享htmlcss 绚丽效果&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495; 文…

python重命名图片

在Python中&#xff0c;获取图片路径并根据当前时间戳重命名图片通常涉及以下几个步骤&#xff1a; 确定图片的原始路径&#xff1a;首先&#xff0c;你需要知道图片的当前位置。获取当前时间戳&#xff1a;然后&#xff0c;使用Python的datetime模块来获取当前时间的时间戳。…

vue3前端开发-小兔鲜项目-使用pinia插件完成token的本地存储

vue3前端开发-小兔鲜项目-使用pinia插件完成token的本地存储&#xff01;实际业务开发中&#xff0c;token是一个表示着用户登录状态的重要信息&#xff0c;它有自己的生命周期。因此&#xff0c;这个参数值必须实例化存储在本地中。不能跟着pinia。因为pinia是基于内存设计的模…

事务和函数索引

事务 事务的定义 事务&#xff08;Transaction&#xff09;&#xff0c;就是将一组SQL语句放在同一批次内去执行&#xff0c;如果一个SQL语句出错&#xff0c;则该批次内 的所有SQL都将被取消执行。 事务的特点 一个事务中如果有一个数据库操作失败&#xff0c;那么整个 事务…

若依框架 : 生成代码

6.生成代码 6.1.配置生成设置 ruoyi-generator -> src -> main -> resources -> generator.yml 由于 案例中 表都有 前缀 为 tta_ , 这里设置去掉 6.2.生成代码 6.2.1.导入数据库中的表 6.2.2.修改设置 6.2.2.1.设置生成信息 点击 编辑 -> 生成信息 特别…

SqlServer 使用T-SQL单纯的复制表数据

今天用Sql Server Manager Studio 先复制一个库建立一个新库出来&#xff0c;各种报错&#xff0c;也不知道是为什么&#xff0c;连备份再还原都不行&#xff0c;算了&#xff0c;自己写个SQL 脚本来执行算了...... 坏处是视图&#xff0c;存储过程和函数都没有复制过来 -- 设…

m4a怎么转mp3?m4a转mp3的几种方法教程

m4a怎么转mp3&#xff1f;M4A音频格式的全称MPEG-4 Audio&#xff0c;是一种音频压缩格式。这种格式以其卓越的音质和相对较小的文件大小而广受欢迎&#xff0c;尤其是在音乐存储、在线流媒体以及音频编辑等领域。M4A格式被广泛应用于苹果公司的产品中&#xff0c;如iPhone、iP…

开放式耳机会成为未来的主流吗?开放式耳机推荐指南

开放式耳机是否会成为未来的主流&#xff0c;是一个值得探讨的问题。 从目前的市场趋势和技术发展来看&#xff0c;有一些因素支持开放式耳机可能成为主流。 一方面&#xff0c;人们对于健康和舒适的关注度不断提高。长时间佩戴传统耳机可能导致耳部不适&#xff0c;而开放式…

在Linux中,部署及优化Tomcat

tomcat概述 自 2017 年 11月编程语言排行榜 Java 占比 13%,高居榜首&#xff0c;Tomcat 也一度成为 Java开发人员的首选。其开源、占用系统资源少、跨平台等特性深受广大程序员喜爱。本章主要学习如何部署 Tomcat 服务&#xff0c;根据生产环境实现多个虚拟主机的配置&#xf…

【QGroundControl二次开发】五.python生成自定义MAVLink消息及使用

一 . 环境配置 参考&#xff1a; MAVLink代码生成-C# 二. 生成MAVLINK协议 在MAVlink源码下找到message_definitions/common.xml&#xff0c;修改其中的内容。 例如&#xff1a; <message id"12" name"DISTANCE_SENSOR"><description>Dedi…

【Code Complete2】Note-1 [启发式编程、管理复杂度、隐藏设计]

【Code Complete2】_Note-1 [启发式编程、管理复杂度、隐藏设计] 文章目录 【Code Complete2】_Note-1 [启发式编程、管理复杂度、隐藏设计]启发式编程管理复杂度隐藏设计--减少“改动所影响的代码量” 启发式编程 ​ **设计是一个启发的过程&#xff0c;充满了不确定性&#…