【设计模式】抽象工厂模式(含与工厂方法模式的对比)
本期我们来学习一下设计模式之抽象工厂模式,在软件开发中,工厂模式 和 抽象工厂模式 都用于创建对象,但它们的应用场景和实现方式有所不同。本文将基于 C++ 代码,分析抽象工厂模式的实现,并对比其与工厂方法模式的区别。
1. 抽象工厂模式简介
抽象工厂模式(Abstract Factory Pattern) 是创建型设计模式,用于创建一系列相关或相互依赖的对象,而无需指定其具体类。它提供了一个接口,允许客户端通过工厂方法创建不同类型的对象,而无需关心具体实现。
适用场景
- 当系统需要创建一系列相关的产品(例如同一风格的桌子和椅子)。
- 希望确保不同产品之间的兼容性(即,现代风格的桌子应当搭配现代风格的椅子)。
- 隐藏对象创建的细节,并遵循开放封闭原则。
2. 代码分析:抽象工厂模式
我们先看一下接下来实现的示例代码UML图:

(1) 产品接口
我们定义了**椅子(Chair)和桌子(Desk)**两个产品接口,并为它们的不同风格(现代、维多利亚)提供具体实现。
class Chair {
public:virtual ~Chair() {}virtual std::string SitOn() const = 0; // 坐在椅子上的行为
};class ModernChair : public Chair {
public:std::string SitOn() const override {return "Sitting on a modern chair.";}
};class VictorianChair : public Chair {
public:std::string SitOn() const override {return "Sitting on a Victorian chair.";}
};
类似地,我们定义了**桌子(Desk)**接口:
class Desk {
public:virtual ~Desk() {}virtual std::string WorkOn() const = 0; // 在桌子上工作的行为virtual std::string PairWithChair(const Chair &collaborator) const = 0; // 桌子与椅子配对
};
每种桌子都可以与同风格的椅子进行搭配:
class ModernDesk : public Desk {
public:std::string WorkOn() const override {return "Working on a modern desk.";}std::string PairWithChair(const Chair &collaborator) const override {return "Pairing modern desk with ( " + collaborator.SitOn() + " )";}
};
(2) 抽象工厂接口
class AbstractFactory {
public:virtual Chair *CreateChair() const = 0;virtual Desk *CreateDesk() const = 0;virtual ~AbstractFactory() {}
};
这个接口定义了创建相关产品的方法。
(3) 具体工厂
具体工厂负责生产特定风格的家具:
class ModernFurnitureFactory : public AbstractFactory {
public:Chair *CreateChair() const override {return new ModernChair();}Desk *CreateDesk() const override {return new ModernDesk();}
};
class VictorianFurnitureFactory : public AbstractFactory {
public:Chair *CreateChair() const override {return new VictorianChair();}Desk *CreateDesk() const override {return new VictorianDesk();}
};
每个工厂都会创建一组相关联的对象(现代风格 or 维多利亚风格)。
(4) 客户端代码
void ClientCode(const AbstractFactory &factory) {const Chair *chair = factory.CreateChair();const Desk *desk = factory.CreateDesk();std::cout << desk->WorkOn() << "\n";std::cout << desk->PairWithChair(*chair) << "\n";delete chair;delete desk;
}
客户端只与抽象工厂接口交互,而不需要知道具体的工厂实现。
int main() {ModernFurnitureFactory *f1 = new ModernFurnitureFactory();ClientCode(*f1);delete f1;VictorianFurnitureFactory *f2 = new VictorianFurnitureFactory();ClientCode(*f2);delete f2;return 0;
}
运行结果:
Client: Testing client code with the first factory type:
Working on a modern desk.
Pairing modern desk with ( Sitting on a modern chair. )Client: Testing the same client code with the second factory type:
Working on a Victorian desk.
Pairing Victorian desk with ( Sitting on a Victorian chair. )
3. 抽象工厂模式 vs. 工厂方法模式
| 比较项 | 抽象工厂模式 | 工厂方法模式 |
|---|---|---|
| 主要作用 | 创建一系列相关对象 | 仅创建单一对象 |
| 产品数量 | 多个相关的产品(如桌子 + 椅子) | 单个产品 |
| 抽象程度 | 提供多个工厂方法 | 仅提供一个工厂方法 |
| 耦合性 | 低,所有产品都由一个工厂创建,保证兼容性 | 低,但每个产品类型需要一个工厂 |
| 适用场景 | 需要确保产品之间的兼容性,例如 UI 组件 | 仅创建某种特定类型的对象 |
示例代码对比
工厂方法模式
class ChairFactory {
public:static Chair* CreateChair() {return new ModernChair(); // 或者 VictorianChair}
};
在工厂方法模式中,每个工厂只负责创建一个对象,而不是一组相关的对象。
抽象工厂模式
class AbstractFactory {
public:virtual Chair *CreateChair() const = 0;virtual Desk *CreateDesk() const = 0;
};
在抽象工厂模式中,一个工厂负责创建一组产品(例如:现代风格的桌子 + 现代风格的椅子)。
4. 什么时候使用抽象工厂模式?
✅ 当多个产品需要搭配使用时
- 例如 GUI 库中,需要同时创建Windows 风格的按钮、窗口,或者Mac 风格的按钮、窗口。
✅ 希望减少依赖并保持代码的可扩展性
- 未来如果要新增 “复古风格家具”,只需创建新的
RetroFurnitureFactory,无需修改原有代码。
✅ 保证产品之间的兼容性
- 例如,现代椅子只能搭配现代桌子,而不能搭配维多利亚桌子。
5. 总结
- 工厂方法模式 只创建单一产品,而 抽象工厂模式 可以创建一组相关产品。
- 抽象工厂模式的优势 在于确保产品之间的兼容性,并降低客户端对具体类的依赖。
- 适用于需要生产一系列相关对象的场景,例如 GUI 组件、数据库驱动等。
如果你的需求仅是创建单个对象,可以使用工厂方法模式。
如果你的需求是创建多个相互关联的对象,建议使用抽象工厂模式。
相关文章:
【设计模式】抽象工厂模式(含与工厂方法模式的对比)
本期我们来学习一下设计模式之抽象工厂模式,在软件开发中,工厂模式 和 抽象工厂模式 都用于创建对象,但它们的应用场景和实现方式有所不同。本文将基于 C 代码,分析抽象工厂模式的实现,并对比其与工厂方法模式的区别。…...
消息队列保证最终一致性的优势
消息队列保证最终一致性的优势 使用消息队列(如Kafka、RabbitMQ等)来实现MySQL和Redis之间的最终一致性,具有以下几个显著优势: 1. 解耦系统组件 降低系统耦合度:生产者(MySQL更新)和消费者&…...
IDEA转战Trae AI IED配置
Trae Ai 的前身是vscode IDEA转战Trae AI IED配置 1.安装java相关的插件 2、安装spring相关的插件 3.配置maven环境 打开 Trae AI IDE -> 首选项 -> 设置 -> Editor 设置 ⚠️配置方式有两种 setting.json文件中直接编辑(推荐)界面设置 方案…...
再学:区块链基础与合约初探 EVM与GAS机制
目录 1.区块链是什么 2.remix 3.账户 4.以太坊三种交易 5.EVM 6.以太坊客户端节点 7.Gas费用 8.区块链浏览器 1.区块链是什么 只需要检验根节点 Merkel根是否有更改,就不用检查每个交易是否有更改。方便很多。 2.remix 3.账户 如果交易失败的话&…...
Nextjs15 - middleware的使用
nextjs 官方文档(current branch 对应如下文档) Middlewarepath-to-regexp 本专栏内容均可在Github:test_05/Middleware 找到 一、middleware 基本使用 中间件允许您在请求完成之前运行代码。然后,根据传入的请求,您…...
PHP If...Else 语句详解
PHP If...Else 语句详解 引言 PHP 是一种流行的服务器端脚本语言,常用于开发动态网站和应用程序。在 PHP 编程中,条件语句是编程逻辑的基础,其中 if...else 语句是最基本且最常用的条件语句之一。本文将详细介绍 PHP 的 if...else 语句&…...
Django之旅:第六节--mysql数据库操作增删改查(二)
前提条件(models.py已经设置好): from django.db import mmodelsclass UserInfo(models.Model):namemodels.CharFIeld(max_length32)passwordmodels.CharFIeld(max_length64)#agemodels.IntegerFIeld()操作数据语法(在views.py文件࿰…...
【SUNO】【AI作词】【提示词】
仿写歌词提示词模板(升级版) 一、仿写目标 风格定位 音乐风格: [填写目标风格,如:民谣/流行/古风/电子/爵士等]参考案例:如《成都》的叙事民谣,《孤勇者》的励志流行。 情感基调: […...
边缘计算 vs. 云计算,谁才是工业物联网的未来?
前言 在物联网(IoT)飞速发展的今天,边缘计算正在彻底改变数据的处理、存储和分析方式。传统的IoT设备数据通常需要发送到云端进行处理,但随着设备数量的激增,这种模式在延迟、带宽和安全性方面暴露出诸多局限。边缘计…...
【Qt】QByteArray详解
QByteArray 是 Qt 框架中用于处理原始字节数据的核心类,其实质可以概括为以下几点: 1. 底层数据结构 • 连续内存块:存储一段连续的字节数据(char*),类似 std::vector<char>,但针对 Qt 框…...
leetcode.189.轮转数组
第一次全反转,第二次反转前k个,第三次反转后n-k个 需要注意的是向又轮转k个时,如果超出数组长度,要对其进行取模运算才是正确的向右轮转个数 class Solution { private:void rotate(vector<int>& nums,int start,int …...
OCR 识别案例
OCR 识别案例 注意点:输入图像尺寸比例尽量和参与模型训练的数据集比例相似,识别效果会更好。 1、pytesseract Pytesseract是一个Python的光学字符识别(OCR)工具,它作为Tesseract OCR引擎的封装,允许你在…...
微信 MMTLS 协议详解(五):加密实现
常用的解密算法,对称非对称 加密,密钥协商, 带消息认证的加解密 #生成RSA 密钥对 void GenerateRsaKeypair(std::string& public_key,std::string& private_key) {RSA* rsa RSA_new();BIGNUM* bn BN_new();// 生成 RSA 密钥对BN_s…...
Mybatis配置文件解析(详细)
引言 在了解Mybatis如何帮助客户进行数据的存取后,便对Mybatis的配置文件起了兴趣,在查阅官方文档后,总结了平时能用到的配置,希望能对大家有帮助 1.核心配置文件 主要是指Mybatis-config.xml中 其包含了会深深影响Mybatis行为…...
有额外限制的 bellman_ford 算法
题目链接 1.有限制的 B e l l m a n _ F o r d Bellman\_Ford Bellman_Ford 时间复杂度: O ( N ∗ M ) O(N*M) O(N∗M) 在传统的 B e l l m a n _ F o r d Bellman\_Ford Bellman_Ford 中,可以处理边数不大于 K K K 条边的最短距离 但我们只要加一条限制(实际…...
深度剖析 Spring 源码 性能优化:核心原理与最佳实践
深度剖析 Spring 源码 & 性能优化:核心原理与最佳实践 🚀 Spring 框架 作为 Java 生态的核心技术,广泛应用于企业级开发。但很多开发者只会“用”Spring,而不深入其内部原理,导致无法高效排查问题 & 进行性能优…...
【BFS】《单源、多源 BFS:图搜索算法的双生力量》
文章目录 前言单源BFS例题一、迷宫中离入口最近的出口二、 最小基因变化三、单词接龙四、为高尔夫比赛砍树 多源BFS例题一、 01 矩阵二、飞地的数量三、地图中的最高点四、地图分析 结语 前言 什么是单源、多源BFS算法问题呢? BFS(Breadth - First Sear…...
【2025】基于springboot+vue的医院在线问诊系统设计与实现(源码、万字文档、图文修改、调试答疑)
基于Spring Boot Vue的医院在线问诊系统设计与实现功能结构图如下: 课题背景 随着互联网技术的飞速发展和人们生活水平的不断提高,传统医疗模式面临着诸多挑战,如患者就医排队时间长、医疗资源分配不均、医生工作压力大等。同时,…...
【前端】原生项目与框架项目区别
不定期更新,建议关注收藏点赞。 使用 HTML CSS JS 和 Vue 或 React 开发的项目各有其优势与不足,适用于不同的场景。目前基本上都采用框架, 总结 何时选择 HTML CSS JS: 适用于 小型项目、简单静态页面、不需要复杂交互 或 …...
STM32基础教程——PWM驱动舵机
目录 前言 技术实现 原理图 接线图 代码实现 内容要点 PWM基本结构 开启外设时钟 配置GPIO端口 配置时基单元 初始化输出比较单元 调整PWM占空比 输出比较通道重映射 舵机角度设置 实验结果 问题记录 前言 舵机(Servo)是一种位置ÿ…...
ThreadLocal详解与高频场景实战指南
ThreadLocal详解与高频场景实战指南 1. ThreadLocal概述 ThreadLocal是Java提供的线程本地变量机制,用于实现线程级别的数据隔离。每个访问该变量的线程都会获得独立的变量副本,适用于需要避免线程间共享数据的场景。 特点: 线程封闭性&a…...
odata 搜索帮助
参考如下链接: FIORI ELement list report 细节开发,设置过滤器,搜索帮助object page跳转等_fiori element label 变量-CSDN博客 注:odata搜索帮助可以直接将值带出来,而不需要进行任何的重定义 搜索帮助metedata配置…...
RK3588开发笔记-RTL8852wifi6模块驱动编译报错解决
目录 前言 一、问题背景 二、驱动编译 总结 前言 在基于 RK3588 进行开发,使用 RTL8852 WiFi6 模块时,遇到了一个让人头疼的驱动编译报错问题:“VFs_internal_I_am_really_a_filesystem_and_am_NoT_a_driver, but does”。经过一番摸索和尝试,最终成功解决了这个问题,在…...
Docker基本命令VS Code远程连接
Docker基本命令 创建自己的docker容器:docker run --net host --name Container_name --gpus all --shm-size 1t -it -v Your_Path:Your_Dir mllm:mac /bin/bashdocker run:用于创建并启动一个新容器-name:为当前新建的容器命名-gpus&#x…...
第二天 开始Unity Shader的学习之旅之熟悉顶点着色器和片元着色器
Shader初学者的学习笔记 第二天 开始Unity Shader的学习之旅之熟悉顶点着色器和片元着色器 文章目录 Shader初学者的学习笔记前言一、顶点/片元着色器的基本结构① Shader "Unity Shaders Book/Chapter 5/ Simple Shader"② SubShader③ CGPROGRAM和ENDCG④ 指明顶点…...
大疆上云api直播功能如何实现
概述 流媒体服务器作为直播画面的中转站,它接收推流端的相机画面,同时拉流端找它获取相机的画面。整个流程如下: 在流媒体服务器上创建流媒体应用(app),一个流媒体服务器上面可以创建多个流媒体应用约定推拉流的地址。假设流媒体服务器工作在1935端口上面,假设创建的流…...
理解文字识别:一文读懂OCR商业化产品的算法逻辑
文字识别是一项“历久弥新”的技术。早在上世纪初,工程师们就开始尝试使用当时有限的硬件设备扫描并识别微缩胶片、纸张上的字符。随着时代和技术的发展,人们在日常生活中使用的电子设备不断更新换代,文字识别的需求成为一项必备的技术基础&a…...
使用 Cursor、MCP 和 Figma 实现工程化项目自动化,提升高达 200% 效率
直接上手不多说其他的! 一、准备动作 1、Cursor下载安卓 1.1访问官方网站 打开您的网络浏览器,访问 Cursor 的官方网站:https://www.cursor.com/cn 1.2开始下载: 点击"Download for free" 根据您的浏览器设置,会自…...
Arduino、ESP32驱动GUVA-S12SD UV紫外线传感器(光照传感器篇)
目录 1、传感器特性 2、控制器和传感器连线图 3、驱动程序 UV紫外线传感器是一个测试紫外线总量的最佳传感器,它不需要使用波长滤波器,只对紫外线敏感。 Arduino UV紫外线传感器,直接输出对应紫外线指数(UV INDEX)的线性电压,输出电压范围大约0~1100mV(对应UV INDEX值…...
PTA 1097-矩阵行平移
给定一个𝑛𝑛nn的整数矩阵。对任一给定的正整数𝑘<𝑛k<n,我们将矩阵的奇数行的元素整体向右依次平移1、……、𝑘、1、……、𝑘、……1、……、k、1、……、k、……个位置,平移…...
