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的设计目的是帮助开发者检测并修复内存错误,如堆栈和全局缓冲区溢出、使用已释放的内存等,这些问题可能…...
React Native在HarmonyOS 5.0阅读类应用开发中的实践
一、技术选型背景 随着HarmonyOS 5.0对Web兼容层的增强,React Native作为跨平台框架可通过重新编译ArkTS组件实现85%以上的代码复用率。阅读类应用具有UI复杂度低、数据流清晰的特点。 二、核心实现方案 1. 环境配置 (1)使用React Native…...

python执行测试用例,allure报乱码且未成功生成报告
allure执行测试用例时显示乱码:‘allure’ �����ڲ����ⲿ���Ҳ���ǿ�&am…...

C++ 设计模式 《小明的奶茶加料风波》
👨🎓 模式名称:装饰器模式(Decorator Pattern) 👦 小明最近上线了校园奶茶配送功能,业务火爆,大家都在加料: 有的同学要加波霸 🟤,有的要加椰果…...
Qt 事件处理中 return 的深入解析
Qt 事件处理中 return 的深入解析 在 Qt 事件处理中,return 语句的使用是另一个关键概念,它与 event->accept()/event->ignore() 密切相关但作用不同。让我们详细分析一下它们之间的关系和工作原理。 核心区别:不同层级的事件处理 方…...
c# 局部函数 定义、功能与示例
C# 局部函数:定义、功能与示例 1. 定义与功能 局部函数(Local Function)是嵌套在另一个方法内部的私有方法,仅在包含它的方法内可见。 • 作用:封装仅用于当前方法的逻辑,避免污染类作用域,提升…...

热烈祝贺埃文科技正式加入可信数据空间发展联盟
2025年4月29日,在福州举办的第八届数字中国建设峰会“可信数据空间分论坛”上,可信数据空间发展联盟正式宣告成立。国家数据局党组书记、局长刘烈宏出席并致辞,强调该联盟是推进全国一体化数据市场建设的关键抓手。 郑州埃文科技有限公司&am…...
大数据治理的常见方式
大数据治理的常见方式 大数据治理是确保数据质量、安全性和可用性的系统性方法,以下是几种常见的治理方式: 1. 数据质量管理 核心方法: 数据校验:建立数据校验规则(格式、范围、一致性等)数据清洗&…...
【实施指南】Android客户端HTTPS双向认证实施指南
🔐 一、所需准备材料 证书文件(6类核心文件) 类型 格式 作用 Android端要求 CA根证书 .crt/.pem 验证服务器/客户端证书合法性 需预置到Android信任库 服务器证书 .crt 服务器身份证明 客户端需持有以验证服务器 客户端证书 .crt 客户端身份…...

基于开源AI智能名片链动2 + 1模式S2B2C商城小程序的沉浸式体验营销研究
摘要:在消费市场竞争日益激烈的当下,传统体验营销方式存在诸多局限。本文聚焦开源AI智能名片链动2 1模式S2B2C商城小程序,探讨其在沉浸式体验营销中的应用。通过对比传统品鉴、工厂参观等初级体验方式,分析沉浸式体验的优势与价值…...

向量几何的二元性:叉乘模长与内积投影的深层联系
在数学与物理的空间世界中,向量运算构成了理解几何结构的基石。叉乘(外积)与点积(内积)作为向量代数的两大支柱,表面上呈现出截然不同的几何意义与代数形式,却在深层次上揭示了向量间相互作用的…...