当前位置: 首页 > news >正文

C++设计模式:抽象工厂模式(风格切换案例)

抽象工厂模式(Abstract Factory)是一种创建型设计模式,其核心思想是:为一组相关或相互依赖的对象提供一个创建接口,而无需指定它们具体的类。简单来说,就是一个工厂可以生产一系列相关的对象。

我们接下来通过逐步拆解、举例说明和详细代码注释来理解这个模式。


1. 什么是抽象工厂模式?

举个简单例子:

想象你是个游戏开发者,需要为游戏开发不同风格的用户界面(UI)。

  • 如果玩家在 Windows 上玩,游戏需要提供 Windows 风格的按钮、文本框。
  • 如果玩家在 Mac 上玩,则需要提供 Mac 风格的按钮、文本框。

这里的问题是:

  • 我们需要根据不同的操作系统来生成一套成对的 UI 组件(按钮 + 文本框)。
  • 不同的 UI 组件风格相互独立,代码却不能依赖具体的组件实现。

解决这个问题的方案就是使用抽象工厂模式。它允许我们将“创建对象”的逻辑与对象的具体实现分离开。


2. 模式的核心要素

抽象工厂模式包含以下几部分:

  1. 抽象工厂:定义创建一系列对象的接口。
  2. 具体工厂:实现创建特定风格对象的工厂。
  3. 抽象产品:定义产品的公共接口(如按钮和文本框的接口)。
  4. 具体产品:特定风格的产品实现。
  5. 客户端:通过抽象工厂创建产品,而不关心具体产品的实现。

下面用代码一步步实现这个设计模式。


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;
};

解释:

  • ButtonTextBox 是两个抽象接口,定义了按钮和文本框的行为(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";}
};

解释:

  • WindowsButtonMacButton 分别实现了 Button 接口。
  • WindowsTextBoxMacTextBox 分别实现了 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 文本框}
};

解释:

  • WindowsFactoryMacFactory 分别实现了 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. 模式的优缺点

优点
  1. 分离具体类:客户端与具体产品的实现解耦。
  2. 确保产品一致性:某个具体工厂生产的所有产品风格一致。
  3. 易于扩展:可以新增工厂和产品系列,而无需修改客户端代码。
缺点
  1. 增加复杂性:需要定义多组接口和类,代码量较多。
  2. 扩展产品族困难:如果需要新增产品(比如菜单),需要修改所有工厂接口和实现。

总结

抽象工厂模式通过工厂接口屏蔽了具体产品的创建细节,使代码更加灵活和可扩展。它非常适合需要生成一组相关对象不希望客户端代码依赖于具体实现的场景。在实际开发中,抽象工厂模式广泛应用于跨平台工具、插件系统等领域。

完整代码实现

以下是抽象工厂模式的完整实现,并将所有输出改为中文形式,便于理解。

#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++设计模式:抽象工厂模式(风格切换案例)

抽象工厂模式&#xff08;Abstract Factory&#xff09;是一种创建型设计模式&#xff0c;其核心思想是&#xff1a;为一组相关或相互依赖的对象提供一个创建接口&#xff0c;而无需指定它们具体的类。简单来说&#xff0c;就是一个工厂可以生产一系列相关的对象。 我们接下来…...

搜维尔科技:Xsens随时随地捕捉,在任何环境下实时录制或捕捉

Xsens随时随地捕捉&#xff0c;在任何环境下实时录制或捕捉 搜维尔科技&#xff1a;Xsens随时随地捕捉&#xff0c;在任何环境下实时录制或捕捉...

爬虫基础总结 —— 附带爬取案例

Crawler —— Learning experience 数据的传输&#xff1a; 在OSI七层模型中&#xff0c;传输层为源主机和目标主机之间提供可靠的数据传输和通信服务&#xff0c;在该层中&#xff0c;有两个重要的协议—— TCP与 UDP协议。 TCP协议&#xff08;传输控制协议&#xff09; …...

图像处理学习笔记-20241118

文章目录 霍夫变换基本原理霍夫变换的步骤使用 OpenCV 实现直线检测示例&#xff1a;标准霍夫变换 示例&#xff1a;概率霍夫变换参数解释霍夫变换检测圆 基于GAN的样本生成GAN的基本原理基于GAN的数据增广流程实现代码示例 同态滤波&#xff08;Homomorphic Filtering&#xf…...

不能打开网页,但能打开QQ、微信(三种方式)

1.VPN错误 下面三个开关全关闭 2.DNS问题 WINR 输入CMD打开命令行 命令行输入 ipconfig/flushdns 重启电脑 3.直接火绒&#xff08;一键修复&#xff09;...

使用 start-local 脚本在本地运行 Elasticsearch

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

计算机网络:概述知识点及习题练习

网课资源&#xff1a; 湖科大教书匠 1、因特网 网络之间需要路由器进行互联&#xff0c;互联网是网络的网络&#xff0c;因特网是最大的互联网&#xff0c;连接到网络的设备称为主机&#xff0c;一般不叫路由器为主机。 因特网发展&#xff1a;ARPNET->三级结构因特网&am…...

python蓝桥杯刷题2

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

在openi平台 基于华为顶级深度计算平台 openmind 动手实践

大家可能一直疑问&#xff0c;到底大模型在哪里有用。 本人从事的大模型有几个方向的业务。 基于生成式语言模型的海事航行警告结构化解析。 基于生成式语言模型的航空航行警告结构化解析。 基于生成式生物序列&#xff08;蛋白质、有机物、rna、dna、mrna&#xff09;的多模态…...

KF UKF

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

中伟视界:AI智能分析算法如何针对非煤矿山的特定需求,提供定制化的安全生产解决方案

非煤矿山智能化改造&#xff0c;除了政策文件&#xff0c;上级监管单位需要安装的AI智能分析算法功能之外的&#xff0c;矿方真正关心的&#xff0c;能解决矿方安全生产隐患的AI智能分析算法功能有哪些呢&#xff1f; 经过与矿方的现场交流沟通&#xff0c;收集第一现场人员对安…...

Unity 编辑器下 Android 平台 Addressable 加载模型粉红色,类似材质丢失

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

Pytest-Bdd-Playwright 系列教程(10):配置功能文件路径 优化场景定义

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

rust逆向初探

rust 逆向葵花宝典 rust逆向技巧 rust逆向三板斧&#xff1a; [!NOTE] 快速定位关键函数 (真正的main函数)&#xff1a;观察输出、输入&#xff0c;字符串搜索&#xff0c;断点等方法。定位关键 加密区 &#xff1a;根据输入的flag&#xff0c;打硬件断点&#xff0c;快速捕获…...

【Linux】apt 关闭 ssl 认证

【注意】apt 关闭 ssl 认证可能会引起软件安装风险&#xff0c;请尽量避免关闭。 执行以下命令可以实现全局关闭 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 代码 思路&#xff1a;领接表存储二叉树&#xff0c;unordered_map存储各个节点对应的值。dfs遍历一下各个子树的大小个数&#xff0c;再写个递归判断是否是对称二叉树&#xff0c;如果是就更新全局答案。 #…...

Unifying Top-down and Bottom-up Scanpath Prediction Using Transformers

Abstract 大多数视觉注意力模型旨在预测自上而下或自下而上的控制&#xff0c;这些控制通过不同的视觉搜索和自由观看任务进行研究。本文提出了人类注意力变换器&#xff08;Human Attention Transformer&#xff0c;HAT&#xff09;&#xff0c;这是一个能够预测两种形式注意力…...

JavaSE(十四)——文件操作和IO

文章目录 文件操作和IO文件相关概念Java操作文件文件系统操作文件内容操作字节流FileOutputStreamFileInputStream代码演示 字符流FileWriterFileReader代码演示 缓冲流转换流 案例练习 文件操作和IO 文件相关概念 文件 通常指的是包含用户数据的文件&#xff0c;如文本文件、…...

【视觉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&#xff08;ASan&#xff09;最初由Google开发&#xff0c;并作为LLVM项目的一部分。ASan的设计目的是帮助开发者检测并修复内存错误&#xff0c;如堆栈和全局缓冲区溢出、使用已释放的内存等&#xff0c;这些问题可能…...

多云管理“拦路虎”:深入解析网络互联、身份同步与成本可视化的技术复杂度​

一、引言&#xff1a;多云环境的技术复杂性本质​​ 企业采用多云策略已从技术选型升维至生存刚需。当业务系统分散部署在多个云平台时&#xff0c;​​基础设施的技术债呈现指数级积累​​。网络连接、身份认证、成本管理这三大核心挑战相互嵌套&#xff1a;跨云网络构建数据…...

OpenLayers 可视化之热力图

注&#xff1a;当前使用的是 ol 5.3.0 版本&#xff0c;天地图使用的key请到天地图官网申请&#xff0c;并替换为自己的key 热力图&#xff08;Heatmap&#xff09;又叫热点图&#xff0c;是一种通过特殊高亮显示事物密度分布、变化趋势的数据可视化技术。采用颜色的深浅来显示…...

使用VSCode开发Django指南

使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架&#xff0c;专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用&#xff0c;其中包含三个使用通用基本模板的页面。在此…...

如何在看板中有效管理突发紧急任务

在看板中有效管理突发紧急任务需要&#xff1a;设立专门的紧急任务通道、重新调整任务优先级、保持适度的WIP&#xff08;Work-in-Progress&#xff09;弹性、优化任务处理流程、提高团队应对突发情况的敏捷性。其中&#xff0c;设立专门的紧急任务通道尤为重要&#xff0c;这能…...

spring:实例工厂方法获取bean

spring处理使用静态工厂方法获取bean实例&#xff0c;也可以通过实例工厂方法获取bean实例。 实例工厂方法步骤如下&#xff1a; 定义实例工厂类&#xff08;Java代码&#xff09;&#xff0c;定义实例工厂&#xff08;xml&#xff09;&#xff0c;定义调用实例工厂&#xff…...

Linux-07 ubuntu 的 chrome 启动不了

文章目录 问题原因解决步骤一、卸载旧版chrome二、重新安装chorme三、启动不了&#xff0c;报错如下四、启动不了&#xff0c;解决如下 总结 问题原因 在应用中可以看到chrome&#xff0c;但是打不开(说明&#xff1a;原来的ubuntu系统出问题了&#xff0c;这个是备用的硬盘&a…...

三体问题详解

从物理学角度&#xff0c;三体问题之所以不稳定&#xff0c;是因为三个天体在万有引力作用下相互作用&#xff0c;形成一个非线性耦合系统。我们可以从牛顿经典力学出发&#xff0c;列出具体的运动方程&#xff0c;并说明为何这个系统本质上是混沌的&#xff0c;无法得到一般解…...

GC1808高性能24位立体声音频ADC芯片解析

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

Web 架构之 CDN 加速原理与落地实践

文章目录 一、思维导图二、正文内容&#xff08;一&#xff09;CDN 基础概念1. 定义2. 组成部分 &#xff08;二&#xff09;CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 &#xff08;三&#xff09;CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 &#xf…...

day36-多路IO复用

一、基本概念 &#xff08;服务器多客户端模型&#xff09; 定义&#xff1a;单线程或单进程同时监测若干个文件描述符是否可以执行IO操作的能力 作用&#xff1a;应用程序通常需要处理来自多条事件流中的事件&#xff0c;比如我现在用的电脑&#xff0c;需要同时处理键盘鼠标…...