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的设计目的是帮助开发者检测并修复内存错误,如堆栈和全局缓冲区溢出、使用已释放的内存等,这些问题可能…...
重置root密码重置
root密码也叫做root密码破解重启虚拟机/启动虚拟机,看到如下界面,快速将鼠标点进虚拟机通过上下箭头,选择第二个选项,然后安装屏幕下方的提示按“e”键进入下一个页面,将光标通过向上向下箭头移动到quiet单词后面&…...
跨境电商选品师口碑如何?网上教你做电商的可信吗?
在跨境电商快速发展的当下,选品师和电商教学成为从业者关注的焦点,其口碑与可信度需理性看待。 跨境电商选品师的口碑呈现两极分化。专业选品师凭借对目标市场消费习惯、政策法规的精准把握,能通过数据分析和市场调研筛选出潜力产品&#x…...
人工智能之数字生命--“骨架真相”
现在这套系统里,安全值 和 服务值 的根需求负责给出总方向,真根任务不会真正“完成”,它们会反复根据当前状态去分解出下一轮阶段任务。真正会完成的,是这些阶段任务下面的叶子子任务。 下面我用你现在代码里的机制,推…...
USB(三)——状态转换与枚举优化
1. USB设备状态转换机制详解 USB设备从插入到正常工作需要经历一系列状态变化,这个过程就像新生婴儿从出生到逐渐适应环境。我调试过上百个USB设备,发现90%的稳定性问题都出现在状态转换阶段。让我们用最接地气的方式拆解这个流程。 1.1 从物理连接到逻辑…...
基于 OpenCV 的银行卡号识别:传统计算机视觉实战详解
计算机视觉(Computer Vision, CV)作为人工智能领域的核心分支,其本质是让机器 “看懂” 图像,将像素信息转化为可理解的语义内容。小到二维码扫描、人脸识别,大到自动驾驶、工业质检,计算机视觉已渗透到生活…...
AgenticAIoT - 自进化智能物联网平台
AgenticAIoT - 自进化智能物联网平台 平台简介 AgenticAIoT 是一款企业级自进化智能物联网平台,深度融合 AI 大模型、物联网(IoT) 与 AI 自主编程 三大核心能力。平台以"智能设备接入 + 数据智能流转 + 规则引擎联动 + AI 决策运维 + 自主进化"为核心理念,提供…...
Qwen3-Reranker-0.6B应用场景:工业设备维修手册与故障现象描述的语义对齐
Qwen3-Reranker-0.6B应用场景:工业设备维修手册与故障现象描述的语义对齐 在工厂车间里,老师傅指着一台嗡嗡异响的数控机床说:“主轴一提速就抖,像踩在弹簧上。”而维修手册里写的却是“主轴轴承预紧力不足导致径向跳动超标”。这…...
一丹一世界FLUX.1镜像免配置教程:Docker一键拉取+端口映射+防火墙开放步骤
一丹一世界FLUX.1镜像免配置教程:Docker一键拉取端口映射防火墙开放步骤 想体验用文字描述就能生成高质量海景美女图的AI绘画吗?今天要介绍的“一丹一世界FLUX.1”镜像,让你无需任何复杂的模型下载、环境配置,只需一条Docker命令…...
n8n子流程调用避坑指南:从数据库写入到模块化开发实战
n8n子流程调用避坑指南:从数据库写入到模块化开发实战 当你开始用n8n构建稍微复杂一点的自动化系统时,很快就会发现把所有逻辑都塞进一个长长的工作流里,不仅维护起来头疼,调试更是噩梦。这时候,子流程调用就成了你工具…...
基于Matlab GUI的手势识别之旅
基于matlab gui的手势识别,导入手部图片,基于肤色模型的颜色分割,去噪,边缘提取,傅立叶算子特征提取,利用最小距离识别手势。最近在研究基于Matlab GUI的手势识别,觉得还挺有趣,来和…...
