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的设计目的是帮助开发者检测并修复内存错误,如堆栈和全局缓冲区溢出、使用已释放的内存等,这些问题可能…...
html中select标签的选项携带多个值
搜索参考资料:SELECT标签中的选项可以携带多个值吗? 【摘抄】: 它可能有一个select选项中的多个值,如下所示。 <select id"ddlEmployee" class"form-control"> <option value"">-- S…...
Lambda表达式如何进行调试
一、概述 Java8提供了lambda表达式,方便我们对数据集合进行操作,我们使用lambda表达式的时候,是不是有这样的疑问,如何对执行过程中的中间数据进行调试呢? 二、例子 在下面的例子中,我们实现随机最多生成…...
C++ —— 剑斩旧我 破茧成蝶—C++11
江河入海,知识涌动,这是我参与江海计划的第2篇。 目录 1. C11的发展历史 2. 列表初始化 2.1 C98传统的{} 2.2 C11中的{} 2.3 C11中的std::initializer_list 3. 右值引用和移动语义 3.1 左值和右值 3.2 左值引用和右值引用 3.3 引用延长生命周期…...
HTML5好看的音乐播放器多种风格(附源码)
文章目录 1.设计来源1.1 音乐播放器风格1效果1.2 音乐播放器风格2效果1.3 音乐播放器风格3效果1.4 音乐播放器风格4效果1.5 音乐播放器风格5效果 2.效果和源码2.1 动态效果2.2 源代码 源码下载万套模板,程序开发,在线开发,在线沟通 作者&…...
C++设计模式行为模式———迭代器模式中介者模式
文章目录 一、引言二、中介者模式三、总结 一、引言 中介者模式是一种行为设计模式, 能让你减少对象之间混乱无序的依赖关系。 该模式会限制对象之间的直接交互, 迫使它们通过一个中介者对象进行合作。 中介者模式可以减少对象之间混乱无序的依赖关系&…...
FFmpeg 4.3 音视频-多路H265监控录放C++开发十五,解码相关,将h264文件进行帧分隔变成avpacket
前提 前面我们学习了 将YUV数据读取到AVFrame,然后将AVFrame通过 h264编码器变成 AVPacket后,然后将avpacket直接存储到了本地就变成了h264文件。 这一节课,学习解码的一部分。我们需要将 本地存储的h264文件进行帧分隔,也就是变…...
力扣 LeetCode 104. 二叉树的最大深度(Day7:二叉树)
解题思路: 采用后序遍历 首先要区别好什么是高度,什么是深度 最大深度实际上就是根节点的高度 高度的求法是从下往上传,从下往上传实际上就是左右中(后序遍历) 深度的求法是从上往下去寻找 所以采用从下往上 本…...
如何高效实现汤臣倍健营销云数据集成到SQLServer
新版订单同步-(Life-Space)江油泰熙:汤臣倍健营销云数据集成到SQL Server 在企业信息化建设中,数据的高效集成和管理是提升业务运营效率的关键。本文将分享一个实际案例——如何通过新版订单同步方案,将汤臣倍健营销云…...
Vue3中使用:deep修改element-plus的样式无效怎么办?
前言:当我们用 vue3 :deep() 处理 elementui 中 el-dialog_body和el-dislog__header 的时候样式一直无法生效,遇到这种情况怎么办? 解决办法: 1.直接在 dialog 上面增加class 我试过,也不起作用,最后用这种…...
具身智能之Isaac Gym使用
0. 简介 Isaac Gym 是由 NVIDIA 提供的一个高性能仿真平台,专门用于大规模的机器人学习和强化学习(RL)任务。它结合了物理仿真、GPU加速、深度学习框架互操作性等特点,使得研究人员和开发者可以快速进行复杂的机器人仿真和训练。…...
可以做ppt的网站有哪些方面/公司主页网站设计
你的得分:100.0完成日期:2014年05月23日17点13分说明:每道小题括号里的答案是您最高分那次所选的答案,标准答案将在本次作业结束(即2014年09月11日)后显示在题目旁边。一、单项选择题。本大题共50个小题,每小题2.0 分&…...
简单公司网站源码/泉州百度网络推广
一、前期准备 单片机:STM32F103ZET6 开发环境:MDK5.14 库函数:标准库V3.5 RGB LED模块:淘宝有售 二、实验效果 8种颜色的LED流水灯,分配见下表,0代表Disable,1代表Enable R G B 0 0 1 0 1 0 …...
wordpress 付款插件/夫唯seo培训
摘要:就计算机辅助教学在解剖学、组织学、病理学、微生物学中的教学应用进行探讨,论述了计算机辅助教学在医学形态学科中理论教学、实验教学及考试中的应用。关键词:医学;形态学科;CAI计算机辅助教学(CAI)系统是以计算…...
中国建设银行校园招聘网站/提升排名
移动端的布局不同于pc端,首先我们要知道在移动端中,css中的1px并不等于物理上的1px,因为手机屏幕的分辨率已经越来越高,高像素但是屏幕尺寸却没有发生太大变化,那就意味着一个物理像素点实际上塞入了好几个像素。 在移…...
网站彩票代理怎么做/抖音seo是什么意思
.NET Core开发日志——Action 原文:.NET Core开发日志——Action在叙述Controller一文中,有一处未做解释,即CreateControllerFactory方法中ControllerActionDescriptor参数是如何产生的。这是因为其与Action的关联性更大,所以放在本文中继续描…...
网站的开发语言有哪些/关键词林俊杰mp3在线听
文章目录(一)inline 内联函数(1)宏函数和内联函数的区别:(2)inline 内联函数的优缺点:(3)使用内联函数建议:(二)inline函数…...