C++设计模式:抽象工厂模式(风格切换案例)
抽象工厂模式(Abstract Factory)是一种创建型设计模式,其核心思想是:为一组相关或相互依赖的对象提供一个创建接口,而无需指定它们具体的类。简单来说,就是一个工厂可以生产一系列相关的对象。
我们接下来通过逐步拆解、举例说明和详细代码注释来理解这个模式。
1. 什么是抽象工厂模式?
举个简单例子:
想象你是个游戏开发者,需要为游戏开发不同风格的用户界面(UI)。
- 如果玩家在 Windows 上玩,游戏需要提供 Windows 风格的按钮、文本框。
- 如果玩家在 Mac 上玩,则需要提供 Mac 风格的按钮、文本框。
这里的问题是:
- 我们需要根据不同的操作系统来生成一套成对的 UI 组件(按钮 + 文本框)。
- 不同的 UI 组件风格相互独立,代码却不能依赖具体的组件实现。
解决这个问题的方案就是使用抽象工厂模式。它允许我们将“创建对象”的逻辑与对象的具体实现分离开。
2. 模式的核心要素
抽象工厂模式包含以下几部分:
- 抽象工厂:定义创建一系列对象的接口。
- 具体工厂:实现创建特定风格对象的工厂。
- 抽象产品:定义产品的公共接口(如按钮和文本框的接口)。
- 具体产品:特定风格的产品实现。
- 客户端:通过抽象工厂创建产品,而不关心具体产品的实现。
下面用代码一步步实现这个设计模式。
3. 详细代码实现与讲解
Step 1: 定义抽象产品
按钮和文本框是两种产品。我们需要定义它们的接口,让不同风格的具体产品实现这些接口。
#include <iostream>
#include <memory> // 用于智能指针// 抽象产品 A: 按钮
class Button {
public:virtual void render() const = 0; // 渲染按钮virtual ~Button() = default; // 虚析构,避免内存泄漏
};// 抽象产品 B: 文本框
class TextBox {
public:virtual void render() const = 0; // 渲染文本框virtual ~TextBox() = default;
};
解释:
Button
和TextBox
是两个抽象接口,定义了按钮和文本框的行为(render()
方法)。- 使用抽象类的好处是,客户端代码可以依赖于接口,而不是具体的实现。
Step 2: 定义具体产品
实现两种风格(Windows 和 Mac)的具体按钮和文本框。
// 具体产品 A1: Windows 风格按钮
class WindowsButton : public Button {
public:void render() const override {std::cout << "Rendering Windows-style Button\n";}
};// 具体产品 B1: Windows 风格文本框
class WindowsTextBox : public TextBox {
public:void render() const override {std::cout << "Rendering Windows-style TextBox\n";}
};// 具体产品 A2: Mac 风格按钮
class MacButton : public Button {
public:void render() const override {std::cout << "Rendering Mac-style Button\n";}
};// 具体产品 B2: Mac 风格文本框
class MacTextBox : public TextBox {
public:void render() const override {std::cout << "Rendering Mac-style TextBox\n";}
};
解释:
WindowsButton
和MacButton
分别实现了Button
接口。WindowsTextBox
和MacTextBox
分别实现了TextBox
接口。- 每个具体类实现了它们特定风格的
render()
方法,输出不同的效果。
Step 3: 定义抽象工厂
工厂负责创建相关产品(按钮 + 文本框)。我们定义一个抽象工厂接口,声明创建按钮和文本框的方法。
// 抽象工厂:定义创建一组产品的方法
class GUIFactory {
public:virtual std::unique_ptr<Button> createButton() const = 0; // 创建按钮virtual std::unique_ptr<TextBox> createTextBox() const = 0; // 创建文本框virtual ~GUIFactory() = default;
};
解释:
GUIFactory
是抽象工厂,定义了创建按钮和文本框的接口。- 返回值使用了
std::unique_ptr
,可以自动管理对象生命周期,防止内存泄漏。
Step 4: 定义具体工厂
实现两个具体工厂,用于创建 Windows 和 Mac 风格的产品。
// 具体工厂 1: Windows 工厂
class WindowsFactory : public GUIFactory {
public:std::unique_ptr<Button> createButton() const override {return std::make_unique<WindowsButton>(); // 创建 Windows 按钮}std::unique_ptr<TextBox> createTextBox() const override {return std::make_unique<WindowsTextBox>(); // 创建 Windows 文本框}
};// 具体工厂 2: Mac 工厂
class MacFactory : public GUIFactory {
public:std::unique_ptr<Button> createButton() const override {return std::make_unique<MacButton>(); // 创建 Mac 按钮}std::unique_ptr<TextBox> createTextBox() const override {return std::make_unique<MacTextBox>(); // 创建 Mac 文本框}
};
解释:
WindowsFactory
和MacFactory
分别实现了GUIFactory
,负责生成各自风格的产品。- 每个工厂实现了
createButton()
和createTextBox()
。
Step 5: 编写客户端代码
客户端通过抽象工厂使用产品,而不关心具体实现。
// 客户端代码:通过工厂创建和使用产品
void renderUI(const GUIFactory& factory) {auto button = factory.createButton(); // 创建按钮auto textBox = factory.createTextBox(); // 创建文本框button->render(); // 渲染按钮textBox->render(); // 渲染文本框
}int main() {std::cout << "Windows GUI:\n";WindowsFactory windowsFactory;renderUI(windowsFactory); // 使用 Windows 工厂std::cout << "\nMac GUI:\n";MacFactory macFactory;renderUI(macFactory); // 使用 Mac 工厂return 0;
}
解释:
renderUI
是客户端函数,通过GUIFactory
创建产品。- 客户端无需关心具体工厂或产品的实现,只依赖于抽象接口。
4. 输出结果
运行上述代码,将输出以下结果:
Windows GUI:
Rendering Windows-style Button
Rendering Windows-style TextBoxMac GUI:
Rendering Mac-style Button
Rendering Mac-style TextBox
5. 模式的优缺点
优点
- 分离具体类:客户端与具体产品的实现解耦。
- 确保产品一致性:某个具体工厂生产的所有产品风格一致。
- 易于扩展:可以新增工厂和产品系列,而无需修改客户端代码。
缺点
- 增加复杂性:需要定义多组接口和类,代码量较多。
- 扩展产品族困难:如果需要新增产品(比如菜单),需要修改所有工厂接口和实现。
总结
抽象工厂模式通过工厂接口屏蔽了具体产品的创建细节,使代码更加灵活和可扩展。它非常适合需要生成一组相关对象且不希望客户端代码依赖于具体实现的场景。在实际开发中,抽象工厂模式广泛应用于跨平台工具、插件系统等领域。
完整代码实现
以下是抽象工厂模式的完整实现,并将所有输出改为中文形式,便于理解。
#include <iostream>
#include <memory> // 用于智能指针管理// 抽象产品 A: 按钮
class Button {
public:virtual void render() const = 0; // 渲染按钮(纯虚函数)virtual ~Button() = default; // 虚析构,防止内存泄漏
};// 抽象产品 B: 文本框
class TextBox {
public:virtual void render() const = 0; // 渲染文本框(纯虚函数)virtual ~TextBox() = default;
};// 具体产品 A1: Windows 风格按钮
class WindowsButton : public Button {
public:void render() const override {std::cout << "渲染 Windows 风格按钮" << "\n"; // 中文输出}
};// 具体产品 B1: Windows 风格文本框
class WindowsTextBox : public TextBox {
public:void render() const override {std::cout << "渲染 Windows 风格文本框" << "\n"; // 中文输出}
};// 具体产品 A2: Mac 风格按钮
class MacButton : public Button {
public:void render() const override {std::cout << "渲染 Mac 风格按钮" << "\n"; // 中文输出}
};// 具体产品 B2: Mac 风格文本框
class MacTextBox : public TextBox {
public:void render() const override {std::cout << "渲染 Mac 风格文本框" << "\n"; // 中文输出}
};// 抽象工厂:定义创建一组产品的方法
class GUIFactory {
public:virtual std::unique_ptr<Button> createButton() const = 0; // 创建按钮virtual std::unique_ptr<TextBox> createTextBox() const = 0; // 创建文本框virtual ~GUIFactory() = default;
};// 具体工厂 1: Windows 工厂
class WindowsFactory : public GUIFactory {
public:std::unique_ptr<Button> createButton() const override {return std::make_unique<WindowsButton>(); // 创建 Windows 按钮}std::unique_ptr<TextBox> createTextBox() const override {return std::make_unique<WindowsTextBox>(); // 创建 Windows 文本框}
};// 具体工厂 2: Mac 工厂
class MacFactory : public GUIFactory {
public:std::unique_ptr<Button> createButton() const override {return std::make_unique<MacButton>(); // 创建 Mac 按钮}std::unique_ptr<TextBox> createTextBox() const override {return std::make_unique<MacTextBox>(); // 创建 Mac 文本框}
};// 客户端代码:通过工厂创建和使用产品
void renderUI(const GUIFactory& factory) {// 使用工厂创建按钮和文本框auto button = factory.createButton(); // 创建按钮auto textBox = factory.createTextBox(); // 创建文本框// 渲染创建的产品button->render();textBox->render();
}int main() {// 使用 Windows 工厂创建产品std::cout << "Windows 界面:" << "\n"; // 中文输出WindowsFactory windowsFactory;renderUI(windowsFactory);// 使用 Mac 工厂创建产品std::cout << "\nMac 界面:" << "\n"; // 中文输出MacFactory macFactory;renderUI(macFactory);return 0;
}
输出结果
运行该程序,输出如下:
Windows 界面:
渲染 Windows 风格按钮
渲染 Windows 风格文本框Mac 界面:
渲染 Mac 风格按钮
渲染 Mac 风格文本框
本文由mdnice多平台发布
相关文章:
C++设计模式:抽象工厂模式(风格切换案例)
抽象工厂模式(Abstract Factory)是一种创建型设计模式,其核心思想是:为一组相关或相互依赖的对象提供一个创建接口,而无需指定它们具体的类。简单来说,就是一个工厂可以生产一系列相关的对象。 我们接下来…...
搜维尔科技:Xsens随时随地捕捉,在任何环境下实时录制或捕捉
Xsens随时随地捕捉,在任何环境下实时录制或捕捉 搜维尔科技:Xsens随时随地捕捉,在任何环境下实时录制或捕捉...

爬虫基础总结 —— 附带爬取案例
Crawler —— Learning experience 数据的传输: 在OSI七层模型中,传输层为源主机和目标主机之间提供可靠的数据传输和通信服务,在该层中,有两个重要的协议—— TCP与 UDP协议。 TCP协议(传输控制协议) …...

图像处理学习笔记-20241118
文章目录 霍夫变换基本原理霍夫变换的步骤使用 OpenCV 实现直线检测示例:标准霍夫变换 示例:概率霍夫变换参数解释霍夫变换检测圆 基于GAN的样本生成GAN的基本原理基于GAN的数据增广流程实现代码示例 同态滤波(Homomorphic Filtering…...

不能打开网页,但能打开QQ、微信(三种方式)
1.VPN错误 下面三个开关全关闭 2.DNS问题 WINR 输入CMD打开命令行 命令行输入 ipconfig/flushdns 重启电脑 3.直接火绒(一键修复)...

使用 start-local 脚本在本地运行 Elasticsearch
警告:请勿将这些说明用于生产部署 本页上的说明仅适用于本地开发。请勿将此配置用于生产部署,因为它不安全。请参阅部署选项以获取生产部署选项列表。 使用 start-local 脚本在 Docker 中快速设置 Elasticsearch 和 Kibana 以进行本地开发或测试。 此设…...

计算机网络:概述知识点及习题练习
网课资源: 湖科大教书匠 1、因特网 网络之间需要路由器进行互联,互联网是网络的网络,因特网是最大的互联网,连接到网络的设备称为主机,一般不叫路由器为主机。 因特网发展:ARPNET->三级结构因特网&am…...

python蓝桥杯刷题2
1.最短路 题解:这个采用暴力枚举,自己数一下就好了 2.门牌制作 题解:门牌号从1到2020,使用for循环遍历一遍,因为range函数无法调用最后一个数字,所以设置成1到2021即可,然后每一次for循环&…...

在openi平台 基于华为顶级深度计算平台 openmind 动手实践
大家可能一直疑问,到底大模型在哪里有用。 本人从事的大模型有几个方向的业务。 基于生成式语言模型的海事航行警告结构化解析。 基于生成式语言模型的航空航行警告结构化解析。 基于生成式生物序列(蛋白质、有机物、rna、dna、mrna)的多模态…...

KF UKF
我需要Kalman 现在,主要是用来处理检测问题情况里的漏检,因为模拟了一段2D, (x,y)的数据,为了看效果,画的线尽量简单一点: import numpy as np import matplotlib.pyplo…...

中伟视界:AI智能分析算法如何针对非煤矿山的特定需求,提供定制化的安全生产解决方案
非煤矿山智能化改造,除了政策文件,上级监管单位需要安装的AI智能分析算法功能之外的,矿方真正关心的,能解决矿方安全生产隐患的AI智能分析算法功能有哪些呢? 经过与矿方的现场交流沟通,收集第一现场人员对安…...

Unity 编辑器下 Android 平台 Addressable 加载模型粉红色,类似材质丢失
Unity 编辑器下 Android 平台 Addressable 加载模型粉红色,类似材质丢失 Addressable Play Mode Script加载模式 选择 Use Existiing Build 1.Unity 切换到 PC 平台,执行 Addressable Build 运行,加载 bundle 内的预制体 显示正常 2.Unit…...

Pytest-Bdd-Playwright 系列教程(10):配置功能文件路径 优化场景定义
Pytest-Bdd-Playwright 系列教程(10):配置功能文件路径 & 优化场景定义 前言一、功能文件路径的配置1.1 全局设置功能文件路径1.2. 在场景中覆盖路径 二、避免重复输入功能文件名2.1 使用方法2.2 functools.partial 的背景 三、应用场景总…...

rust逆向初探
rust 逆向葵花宝典 rust逆向技巧 rust逆向三板斧: [!NOTE] 快速定位关键函数 (真正的main函数):观察输出、输入,字符串搜索,断点等方法。定位关键 加密区 :根据输入的flag,打硬件断点,快速捕获…...
【Linux】apt 关闭 ssl 认证
【注意】apt 关闭 ssl 认证可能会引起软件安装风险,请尽量避免关闭。 执行以下命令可以实现全局关闭 sll 验证。 echo Acquire::https::Verify-Peer "false"; >> /etc/apt/apt.conf.d/99disable-signature-verificationecho Acquire::https::Verif…...
【算法】P5018 对称二叉树
题目 P5018 对称二叉树 https://www.luogu.com.cn/problem/P5018 代码 思路:领接表存储二叉树,unordered_map存储各个节点对应的值。dfs遍历一下各个子树的大小个数,再写个递归判断是否是对称二叉树,如果是就更新全局答案。 #…...

Unifying Top-down and Bottom-up Scanpath Prediction Using Transformers
Abstract 大多数视觉注意力模型旨在预测自上而下或自下而上的控制,这些控制通过不同的视觉搜索和自由观看任务进行研究。本文提出了人类注意力变换器(Human Attention Transformer,HAT),这是一个能够预测两种形式注意力…...

JavaSE(十四)——文件操作和IO
文章目录 文件操作和IO文件相关概念Java操作文件文件系统操作文件内容操作字节流FileOutputStreamFileInputStream代码演示 字符流FileWriterFileReader代码演示 缓冲流转换流 案例练习 文件操作和IO 文件相关概念 文件 通常指的是包含用户数据的文件,如文本文件、…...
【视觉SLAM】4b-特征点法估计相机运动之PnP 3D-2D
文章目录 0. 前言1. PnP求解1.1 直接线性变换DLT1.2 P3P1.3 光束平差法BA2. 实现0. 前言 透视n点(Perspective-n-Point,PnP)问题是计算机视觉领域的经典问题,用于求解3D-2D的点运动。换句话说,当知道 N N N个世界坐标系中3D空间点的坐标以及它们在图像上的投影点像素坐标…...
android 性能分析工具(04)Asan 内存检测工具
1 Asan工具简介 1.1 Asan工具历史背景 AddressSanitizer(ASan)最初由Google开发,并作为LLVM项目的一部分。ASan的设计目的是帮助开发者检测并修复内存错误,如堆栈和全局缓冲区溢出、使用已释放的内存等,这些问题可能…...
树莓派超全系列教程文档--(62)使用rpicam-app通过网络流式传输视频
使用rpicam-app通过网络流式传输视频 使用 rpicam-app 通过网络流式传输视频UDPTCPRTSPlibavGStreamerRTPlibcamerasrc GStreamer 元素 文章来源: http://raspberry.dns8844.cn/documentation 原文网址 使用 rpicam-app 通过网络流式传输视频 本节介绍来自 rpica…...
DeepSeek 赋能智慧能源:微电网优化调度的智能革新路径
目录 一、智慧能源微电网优化调度概述1.1 智慧能源微电网概念1.2 优化调度的重要性1.3 目前面临的挑战 二、DeepSeek 技术探秘2.1 DeepSeek 技术原理2.2 DeepSeek 独特优势2.3 DeepSeek 在 AI 领域地位 三、DeepSeek 在微电网优化调度中的应用剖析3.1 数据处理与分析3.2 预测与…...
React Native 导航系统实战(React Navigation)
导航系统实战(React Navigation) React Navigation 是 React Native 应用中最常用的导航库之一,它提供了多种导航模式,如堆栈导航(Stack Navigator)、标签导航(Tab Navigator)和抽屉…...

云启出海,智联未来|阿里云网络「企业出海」系列客户沙龙上海站圆满落地
借阿里云中企出海大会的东风,以**「云启出海,智联未来|打造安全可靠的出海云网络引擎」为主题的阿里云企业出海客户沙龙云网络&安全专场于5.28日下午在上海顺利举办,现场吸引了来自携程、小红书、米哈游、哔哩哔哩、波克城市、…...
QMC5883L的驱动
简介 本篇文章的代码已经上传到了github上面,开源代码 作为一个电子罗盘模块,我们可以通过I2C从中获取偏航角yaw,相对于六轴陀螺仪的yaw,qmc5883l几乎不会零飘并且成本较低。 参考资料 QMC5883L磁场传感器驱动 QMC5883L磁力计…...

前端导出带有合并单元格的列表
// 导出async function exportExcel(fileName "共识调整.xlsx") {// 所有数据const exportData await getAllMainData();// 表头内容let fitstTitleList [];const secondTitleList [];allColumns.value.forEach(column > {if (!column.children) {fitstTitleL…...

江苏艾立泰跨国资源接力:废料变黄金的绿色供应链革命
在华东塑料包装行业面临限塑令深度调整的背景下,江苏艾立泰以一场跨国资源接力的创新实践,重新定义了绿色供应链的边界。 跨国回收网络:废料变黄金的全球棋局 艾立泰在欧洲、东南亚建立再生塑料回收点,将海外废弃包装箱通过标准…...

GC1808高性能24位立体声音频ADC芯片解析
1. 芯片概述 GC1808是一款24位立体声音频模数转换器(ADC),支持8kHz~96kHz采样率,集成Δ-Σ调制器、数字抗混叠滤波器和高通滤波器,适用于高保真音频采集场景。 2. 核心特性 高精度:24位分辨率,…...

并发编程 - go版
1.并发编程基础概念 进程和线程 A. 进程是程序在操作系统中的一次执行过程,系统进行资源分配和调度的一个独立单位。B. 线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。C.一个进程可以创建和撤销多个线程;同一个进程中…...

阿里云Ubuntu 22.04 64位搭建Flask流程(亲测)
cd /home 进入home盘 安装虚拟环境: 1、安装virtualenv pip install virtualenv 2.创建新的虚拟环境: virtualenv myenv 3、激活虚拟环境(激活环境可以在当前环境下安装包) source myenv/bin/activate 此时,终端…...