C++ 程序设计:四大模式(工厂+装饰+策略+观察者)

1.前言

"工厂+装饰+策略+观察者"是常见且常用的设计模式之一,但并不是指称"四大模式"的官方术语。

"四大模式"通常是指指令式面向对象编程中的四个基本概念:封装继承多态抽象。这四个概念是面向对象编程的基石。

2.工厂模式

工厂模式(例:工厂方法模式)中,通常存在一个抽象的工厂类和多个具体的工厂子类。每个工厂子类负责创建一种具体的对象,具体的创建逻辑在工厂子类中实现。客户端通过与工厂接口进行交互,并由工厂子类来创建所需的对象。

/* 将工厂也声明成一个基类 */             
class TaskBase
{
public:TaskBase();virtual ~CalculatorBase();virtual void exec(unsigned char *data) = 0; 
private:...
};class TaskA : public TaskBase
{
public:TaskA();~TaskA();virtual void exec(unsigned char *data) override;
private:...
};class TaskB : public TaskBase
{
public:TaskA();~TaskB();virtual void exec(unsigned char *data) override;
private:...
};int main() 
{TaskBase *task_1 = new TaskA();TaskBase *task_2 = new TaskB();TaskBase *current;int type = 1; // 进行工厂的选择if(type == 0)current = task_1;elsecurrent = task_2;current->exec();return 0;
}

这种模式特别适合task任务的选择 

3.装饰模式

装饰器模式是一种结构型设计模式,用于动态地给对象添加额外的行为,同时又不修改原始对象的结构。装饰器模式通过将对象包装在一个具有相同接口的装饰器类中,可以在运行时添加、修改或删除对象的功能。

装饰器模式的核心思想是使用继承和组合,通过创建具有相同接口的装饰器类来包装原始对象。装饰器类拥有与原始对象相同的接口,并持有一个指向原始对象的引用。装饰器类可以在执行原始对象的方法前后添加自定义的行为,以扩展原始对象的功能。

#include <iostream>
#include <string>// 抽象订单接口
class Order 
{
public:virtual void process() = 0;
};// 具体订单类
class SimpleOrder : public Order 
{
public:void process() override {std::cout << "处理简单订单" << std::endl;}
};// 抽象装饰器类
class OrderDecorator : public Order 
{
protected:Order* decoratedOrder;  // 持有一个指向原始订单的引用public:OrderDecorator(Order* order) : decoratedOrder(order) {}void process() override {decoratedOrder->process();}
};// 具体装饰器类A
class UrgentOrderDecorator : public OrderDecorator 
{
public:UrgentOrderDecorator(Order* order) : OrderDecorator(order) {}void process() override {OrderDecorator::process();std::cout << "添加急件处理" << std::endl;}
};// 具体装饰器类B
class InternationalOrderDecorator : public OrderDecorator 
{
public:InternationalOrderDecorator(Order* order) : OrderDecorator(order) {}void process() override{OrderDecorator::process();std::cout << "添加国际订单处理" << std::endl;}
};int main() 
{Order* order = new SimpleOrder();order->process();  // 处理简单订单Order* urgentOrder = new UrgentOrderDecorator(order);urgentOrder->process();  // 处理简单订单,并添加急件处理Order* internationalOrder = new InternationalOrderDecorator(order);internationalOrder->process();  // 处理简单订单,并添加国际订单处理Order* urgentInternationalOrder = new UrgentOrderDecorator(new InternationalOrderDecorator(order));urgentInternationalOrder->process();  // 处理简单订单,并同时添加国际订单和急件处理delete urgentInternationalOrder;delete internationalOrder;delete urgentOrder;delete order;return 0;
}

在这个例子中,抽象订单接口Order定义了订单的处理方法process()SimpleOrder是具体订单类,它实现了订单的处理逻辑。OrderDecorator是抽象装饰器类,它持有一个指向原始订单的引用,并在订单处理前后添加额外的功能。UrgentOrderDecoratorInternationalOrderDecorator是具体装饰器类,分别给订单添加了急件处理和国际订单处理。

客户端代码演示了不同的订单处理情况。通过创建不同的装饰器对象来包装原始订单对象,可以在处理订单时添加额外的功能。每个装饰器类都在执行原始订单的处理方法前后添加了自己特定的行为。

这个例子展示了装饰器模式的灵活性,可以在不修改订单类的情况下,动态地添加、修改或删除订单的功能。它允许通过装饰器组合来创建不同的订单处理方式。

处理简单订单处理简单订单
添加急件处理处理简单订单
添加国际订单处理处理简单订单
添加国际订单处理
添加急件处理
处理简单订单
添加急件处理
添加国际订单处理

 4.策略模式

C++策略模式(Strategy Pattern)是一种行为设计模式,它允许在运行时动态地选择算法或行为。该模式将算法封装在独立的策略类中,使得它们可以相互替换,而不影响客户端代码。这种灵活性使得我们可以根据不同的情境选择不同的算法,提高代码的复用性和可维护性。 

#include <iostream>// 定义抽象策略接口
class SendStrategy 
{
public:virtual void sendData(const std::string& data) const = 0;
};// 定义具体策略类:UART发送
class UartStrategy : public SendStrategy 
{
public:void sendData(const std::string& data) const override {std::cout << "UART发送数据:" << data << std::endl;}
};// 定义具体策略类:TCP发送
class TcpStrategy : public SendStrategy 
{
public:void sendData(const std::string& data) const override {std::cout << "TCP发送数据:" << data << std::endl;}
};// 定义具体策略类:UDP发送
class UdpStrategy : public SendStrategy 
{
public:void sendData(const std::string& data) const override {std::cout << "UDP发送数据:" << data << std::endl;}
};// 定义环境类
class DataSender {
private:SendStrategy* strategy;public:explicit DataSender(SendStrategy* strategy) : strategy(strategy) {}void setStrategy(SendStrategy* strategy) {this->strategy = strategy;}void sendData(const std::string& data) {if (strategy) {strategy->sendData(data);}}
};int main() 
{// 创建具体策略对象SendStrategy* uart = new UartStrategy();SendStrategy* tcp = new TcpStrategy();SendStrategy* udp = new UdpStrategy();// 创建环境对象并设置初始策略DataSender sender(uart);// 发送数据sender.sendData("Hello, World!");// 切换为TCP发送sender.setStrategy(tcp);sender.sendData("Hello, TCP!");// 切换为UDP发送sender.setStrategy(udp);sender.sendData("Hello, UDP!");// 释放资源delete uart;delete tcp;delete udp;return 0;
}

我们首先定义了一个抽象策略接口SendStrategy,然后实现了三个具体的策略类UartStrategyTcpStrategyUdpStrategy,分别用于UART、TCP和UDP发送。

接着,我们定义了环境类DataSender,它维护了一个指向具体策略对象的引用,并提供了一个公共接口sendData()以供客户端代码调用。

在主函数中,我们创建了具体策略对象,并将初始策略设置为UART发送。然后,我们使用环境对象的sendData()方法来发送数据。

接着,我们切换策略为TCP发送和UDP发送,并分别调用环境对象的sendData()方法。

5.观察者模式

C++观察者模式(Observer Pattern)是一种行为设计模式,它使用一对多的依赖关系,当一个对象(主题)的状态发生变化时,自动通知并更新其他依赖于它的对象(观察者)。观察者模式提供了一种松耦合的方式,使得主题和观察者可以独立变化,而不需要相互了解具体的实现细节。

观察者模式的主要角色包括:

  1. Subject(主题):它通常是一个抽象类或接口,定义了增加、删除和通知观察者的方法。
  2. ConcreteSubject(具体主题):它是主题的具体实现,存储了具体观察者对象,并在状态发生变化时通知观察者。
  3. Observer(观察者):它定义了观察者接收通知并更新自己的方法。
  4. ConcreteObserver(具体观察者):它是观察者的具体实现,实现了接收通知和更新自身的方法。
#include <iostream>
#include <vector>// 定义观察者接口
class Observer {
public:virtual void update(const std::string& news) = 0;
};// 定义具体观察者类
class Subscriber : public Observer {
private:std::string name;public:explicit Subscriber(const std::string& name) : name(name) {}void update(const std::string& news) override {std::cout << name << " 收到新闻:" << news << std::endl;}
};// 定义主题类
class NewsChannel {
private:std::vector<Observer*> observers;std::string latestNews;public:void addObserver(Observer* observer) {observers.push_back(observer);}void removeObserver(Observer* observer) {// 在实际应用中可能需要实现删除观察者的逻辑}void notifyObservers() {for (auto observer : observers) {observer->update(latestNews);}}void setNews(const std::string& news) {latestNews = news;notifyObservers();}
};int main() {// 创建主题类(新闻频道)NewsChannel newsChannel;// 创建观察者类(订阅者)Observer* subscriber1 = new Subscriber("订阅者1");Observer* subscriber2 = new Subscriber("订阅者2");// 添加观察者到主题中newsChannel.addObserver(subscriber1);newsChannel.addObserver(subscriber2);// 发布新闻newsChannel.setNews("今天天气晴朗,适合出游!");// 释放资源delete subscriber1;delete subscriber2;return 0;
}

拓展:委托机制下的观察者模式

#include <iostream>
#include <vector>
#include <thread>
#include <mutex>// 定义一个委托类
template<typename... Args>
class Delegate {
public:using FunctionPtr = std::function<void(Args...)>;void Add(FunctionPtr function) {std::lock_guard<std::mutex> lock(mutex);delegates.push_back(function);}void Remove(FunctionPtr function) {std::lock_guard<std::mutex> lock(mutex);delegates.erase(std::remove(delegates.begin(), delegates.end(), function), delegates.end());}void Invoke(Args... args) {std::lock_guard<std::mutex> lock(mutex);for (auto delegate : delegates) {delegate(args...);}}private:std::vector<FunctionPtr> delegates;std::mutex mutex;
};// 定义观察者基类
class Observer {
public:virtual void Notify() = 0;
};// 定义具体观察者类
class ConcreteObserver : public Observer {
public:ConcreteObserver(Delegate<void>& delegate) : delegate(delegate) {}void Notify() override {// 当前线程接收到通知后的处理代码std::cout << "ConcreteObserver received notification in thread: " << std::this_thread::get_id() << std::endl;}private:Delegate<void>& delegate;
};// 定义主题类
class Subject {
public:void AddObserver(Observer* observer) {std::lock_guard<std::mutex> lock(mutex);observers.push_back(observer);}void RemoveObserver(Observer* observer) {std::lock_guard<std::mutex> lock(mutex);observers.erase(std::remove(observers.begin(), observers.end(), observer), observers.end());}void NotifyObservers() {std::lock_guard<std::mutex> lock(mutex);for (auto observer : observers) {observer->Notify();}}private:std::vector<Observer*> observers;std::mutex mutex;
};// 工作线程函数
void WorkerThread(Subject& subject) {// 在工作线程中执行一些任务...// 完成后通知观察者std::this_thread::sleep_for(std::chrono::seconds(2)); // 模拟工作subject.NotifyObservers();
}int main() {// 创建委托对象Delegate<void> delegate;// 创建观察者对象ConcreteObserver observer(delegate);// 创建主题对象Subject subject;// 将观察者注册到主题中subject.AddObserver(&observer);// 创建工作线程并传递主题对象std::thread workerThread(WorkerThread, std::ref(subject));// 主线程中执行其他任务std::cout << "Main thread doing some work..." << std::endl;std::this_thread::sleep_for(std::chrono::seconds(1));// 等待工作线程结束workerThread.join();return 0;
}

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

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

相关文章

Ubuntu 命令行编辑文件后如何保存退出

在 Ubuntu 命令行中编辑文件后&#xff0c;可以使用以下步骤保存并退出&#xff1a; 按下键盘上的 Ctrl 键和 X 键组合&#xff0c;以退出编辑模式。如果文件已更改&#xff0c;你将看到提示&#xff0c;询问是否保存更改。按下 Y 键来确认保存更改&#xff0c;或按下 N 键取消…

4、深入理解ribbon

一、负载均衡的两种方式 服务器端负载均衡 传统的方式前端发送请求会到我们的的nginx上去&#xff0c;nginx作为反向代理&#xff0c;然后路由给后端的服务器&#xff0c;由于负载均衡算法是nginx提供的&#xff0c;而nginx是部署到服务器端的&#xff0c;所以这种方式又被称为…

linux之Ubuntu系列(-)常见指令 重定向

Ubuntu 中文 版本 注意点 通过修改语言改成英文 在终端录入&#xff1a;export LANGen_US 在终端录入&#xff1a;xdg-user-dirs-gtk-update 单用户和多用户 命令格式 command [-选项] [参数] –查看命令的帮助 命令 --help man 命令 |操作键| 功能| |空格键|-显示手册的下…

合并修改缺陷分支的commit到master分支

合并修改缺陷分支的commit到master分支 当我们在修改缺陷的时候&#xff0c;我们会开辟一个分支&#xff0c;专门用来修改缺陷例如hotfix 当我们在hotfix上commit的代码&#xff0c;想要合并到master分支&#xff0c;这时我们要怎么处理呢 我们使用 git cherry-pick&#xff1…

OSS对象存储后端实现+Vue实现图片上传【基于若依管理系统开发】

文章目录 基本介绍术语介绍图片上传方式介绍普通上传用户直传应用服务器签名后直传 OSS对象存储后端实现maven配置文件配置类ServiceController 图片上传前端图片上传组件api页面使用组件组件效果 基本介绍 术语介绍 Bucket&#xff08;存储空间&#xff09;&#xff1a;用于…

【论文】基于GANs的图像文字擦除 ——2010.EraseNet: End-to-End Text Removal in the Wild(已开源)

pytorch官方代码&#xff1a;https://github.com/lcy0604/EraseNet 论文&#xff1a;2010.EraseNet: End-to-End Text Removal in the Wild 网盘提取码&#xff1a;0719 一、图片文字去除效果 图10 SCUT-EnsText 真实数据集的去除 第一列原图带文字、第二列为去除后的标签&a…

爆肝整理,Postman接口测试-全局变量/接口关联/加密/解密(超细)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 全局变量和环境变…

[华为OD] 污染水域(多源BFS)

救命&#xff0c;因为实在找不到工作。。。 所以已经开始准备华为OD的笔试题了。。。 但是内心深处不是很想去OD呜呜呜 文章目录 BFS与多源BFS污染水域leetcode 1162:&#xff1a;地图分析leetcode 542&#xff1a;01矩阵leetcode 1020&#xff1a;飞地的数量 BFS与多源BFS BF…

AJAX:宏任务与微任务

异步任务划分为了 宏任务&#xff1a;由浏览器环境执行的异步代码 微任务&#xff1a;由 JS 引擎环境执行的异步代码 宏任务和微任务具体划分&#xff1a; 左边表格是宏任务&#xff0c;右边是微任务 事件循环模型 /*** 目标&#xff1a;阅读并回答打印的执行顺序 */ console…

Spark编程-键值对RDD(K,V)创建及常用操作

简述 SparkRDD中可以包含任何类型的对象&#xff0c;在实际应用中&#xff0c;“键值对”是一种比较常见的RDD元素类型&#xff0c;分组和聚合操作中经常会用到&#xff0c;尤其是groupByKey和reduceByKey。 Spark操作中经常会用到“键值对RDD”&#xff08;Pair RDD&a…

CSS样式

1.高度和宽度 .c1{height:300px;width:500px;}注意事项&#xff1a; 宽度支持百分比&#xff0c;高度不支持。行内标签&#xff1a;默认无效会计标签&#xff1a;默认有效&#xff08;霸道&#xff0c;右侧区域空白&#xff0c;也不给你用&#xff09; 2.块级和行内标签 块…

【Django学习】(十四)自定义action_router

之前我们的视图类可以继承GenericViewSet或者ModelViewSet&#xff0c;我们不用再自定义通用的action方法&#xff0c;但是有时候我们需要自定义action&#xff0c;我们该如何设计呢&#xff1f; 自定义action 1、手写视图逻辑 1.1、先在视图集里自定义action方法&#xff0…

HCIP第十二天(笔记)

企业网的三层架构 园区 --- 工厂、政府机关、商城、校园、公园等公共场所&#xff0c;为了实现数据的互通&#xff0c;所搭建的网络都可以称为是园区网 接入层 使用二层交换机 --- 依靠MAC地址表进行转发的设备 WLAN --- 无线局域网 --- 广义上指以无线电波、激光…

GO语言泛型

set一般没什么不方便的 但是使用GET 需要使用类型断言,将取出来的数据转为预期数据, 空接口本身是一个装箱,会产生内存逃逸和多一部分空间. 于是1.17GO使用泛型. 泛型实现: 分析可执行文件后:发现 也就是泛型会为每个数据类型都生产一套代码,导致可执行文件大小增加,并且使用…

uni-app中a标签下载文件跳转后左上角默认返回键无法继续返回

1.首先使用的是onBackPress //跟onShow同级别 onBackPress(option){ uni.switchTab({ url:/pages/....... return true }) }发现其在uni默认头部中使用是可以的 但是h5使用了"navigationStyle":"custom"后手机默认的返回并不可以&#xff0c; 2.经过查询…

LCD-STM32液晶显示中英文-(5.字符编码)

目录 字符编码 字符编码说明参考网站 字符编码 ASCII编码 ASCII编码介绍 ASCII编码表 中文编码 1. GB2312标准 区位码 2. GBK编码 3. GB18030 各个标准的对比说明 4. Big5编码 字符编码 字符编码说明参考网站 字符编码及转换测试&#xff1a;导航菜单 - 千千秀字 …

智迪科技在创业板上市:市值约31亿元,谢伟明和黎柏松为实控人

7月17日&#xff0c;珠海市智迪科技股份有限公司&#xff08;下称“智迪科技”&#xff0c;SZ:301503&#xff09;在深圳证券交易所创业板上市。本次上市&#xff0c;智迪科技的发行价为31.59元/股&#xff0c;发行数量为2000万股&#xff0c;募资总额约为6.32亿元&#xff0c;…

onnx如何改变输入的维度

最近遇到一个难题&#xff0c;就算在用行为识别onnx转rknn的时候提示维度不对&#xff0c;因为行为识别模型是5维的。而rknn只支持4维。 我们先加载模型看一下它的input和node 可以看出模型的input[1]是一个全连接&#xff0c;因此我们可以直接修改他的input[0] input hel…

Kafka 入门到起飞系列 - 生产者发送消息流程解析

生产者通过producerRecord 对象封装消息主题、消息的value&#xff08;内容&#xff09;、timestamp(时间戳)等 生产者通过send()方法发送消息&#xff0c;send()方法会经过如下几步 1. 首先将消息交给拦截器&#xff08;Interceptor&#xff09;处理, 拦截器对生产者而言&…

静态数码管——FPGA

文章目录 前言一、数码管1、数码管简介2、共阴极数码管or共阳极数码管3、共阴极与共阳极的真值表 二、系统设计1、模块框图2、RTL视图 三、源码1、seg_led_static模块2、time_count模块3、top_seg_led_static(顶层文件) 四、效果五、总结六、参考资料 前言 环境&#xff1a; 1、…