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

设计模式 之 工厂模式(简单工厂模式、工厂方法模式、抽象工厂模式)(C++)

文章目录

  • C++ 工厂模式
    • 引言
    • 一、简单工厂模式
      • 概念
      • 实现步骤
      • 示例代码
      • 优缺点
    • 二、工厂方法模式
      • 概念
      • 实现步骤
      • 示例代码
      • 优缺点
    • 三、抽象工厂模式
      • 概念
      • 实现步骤
      • 示例代码
      • 优缺点

C++ 工厂模式

引言

在 C++ 编程中,对象的创建是一个常见且基础的操作。然而,当项目规模逐渐增大,对象的创建逻辑变得复杂时,直接在代码中使用 new 关键字创建对象会带来诸多问题,比如代码的可维护性变差、难以扩展等。工厂模式应运而生,它为对象的创建提供了一种更加灵活、可扩展的解决方案。本文将详细介绍 C++ 中的工厂模式,包括简单工厂模式、工厂方法模式和抽象工厂模式,并通过具体的例子帮助大家理解。

一、简单工厂模式

概念

简单工厂模式是工厂模式的基础版本,它定义了一个工厂类,该类可以根据传入的参数决定创建并返回哪种产品类的实例。简单来说,就是把对象的创建逻辑封装在一个工厂类中。

实现步骤

  1. 定义产品基类:创建一个抽象的产品基类,所有具体产品类都要继承这个基类。
  2. 创建具体产品类:实现产品基类的接口,创建具体的产品类。
  3. 创建工厂类:在工厂类中定义一个创建产品的方法,根据传入的参数决定创建哪种具体产品。

示例代码

#include<iostream>
#include<memory>// 定义水果抽象基类,包含纯虚函数 name
class Fruit{public:// 纯虚函数,用于输出水果名称,派生类需实现virtual void name()=0;
};// 苹果类,继承自 Fruit 类
class Apple:public Fruit{public:// 重写基类的 name 函数,输出苹果名称void name() override{std::cout<<"Apple"<<std::endl;}
};// 香蕉类,继承自 Fruit 类
class Banana:public Fruit{public:// 重写基类的 name 函数,输出香蕉名称void name() override{std::cout<<"Banana"<<std::endl;}
};// 工厂类,用于创建不同类型的水果对象
class Factory{public:// 静态方法,根据传入的水果名称创建对应的水果对象static std::unique_ptr<Fruit> createFruit(std::string fruit_name){if(fruit_name=="apple"){// 创建苹果对象并返回其 unique_ptrreturn std::unique_ptr<Fruit>(new Apple());}else if(fruit_name=="banana"){// 创建香蕉对象并返回其 unique_ptrreturn std::unique_ptr<Fruit>(new Banana());}else{// 若名称不匹配,返回空指针return nullptr;}}
};int main()
{// 使用工厂类创建苹果对象std::unique_ptr<Fruit> fruit = Factory::createFruit("apple");// 调用苹果对象的 name 函数输出名称fruit->name();  // 使用工厂类创建香蕉对象fruit = Factory::createFruit("banana");// 调用香蕉对象的 name 函数输出名称fruit->name();// 再次使用工厂类创建苹果对象fruit = Factory::createFruit("apple");return 0;
}

优缺点

  • 优点:实现简单,将对象的创建和使用分离,提高了代码的可维护性。
  • 缺点:工厂类职责过重,违反了开闭原则(对扩展开放,对修改关闭)。如果需要新增产品,就需要修改工厂类的代码。

二、工厂方法模式

概念

工厂方法模式是在简单工厂模式的基础上进行了改进,它将创建对象的具体逻辑延迟到子类中实现。定义一个创建对象的抽象方法,让子类决定实例化哪个具体产品类。

实现步骤

  1. 定义产品基类:同简单工厂模式。
  2. 创建具体产品类:同简单工厂模式。
  3. 定义抽象工厂类:定义一个抽象的工厂类,其中包含一个抽象的创建产品的方法。
  4. 创建具体工厂类:继承抽象工厂类,实现创建产品的方法,决定创建哪种具体产品。

示例代码

#include<iostream>
#include<memory>// 定义抽象基类 Fruit,包含纯虚函数 name
// 任何继承自该类的具体水果类都必须实现 name 函数
class Fruit {
public:// 纯虚函数,用于输出水果名称virtual void name() = 0;
};// 定义 Apple 类,继承自 Fruit 类
class Apple : public Fruit {
public:// 重写基类的纯虚函数 name,输出苹果名称void name() override {std::cout << "Apple" << std::endl;}
};// 定义 Banana 类,继承自 Fruit 类
class Banana : public Fruit {
public:// 重写基类的纯虚函数 name,输出香蕉名称void name() override {std::cout << "Banana" << std::endl;}
};// 定义抽象工厂类 Factory,包含纯虚函数 create
// 具体的工厂类需要实现该函数来创建水果对象
class Factory {
public:// 纯虚函数,用于创建水果对象virtual std::shared_ptr<Fruit> create() = 0;
};// 定义 AppleFactory 类,继承自 Factory 类
class AppleFactory : public Factory {
public:// 重写基类的纯虚函数 create,创建苹果对象std::shared_ptr<Fruit> create() override {return std::make_shared<Apple>();}
};// 定义 BananaFactory 类,继承自 Factory 类
class BananaFactory : public Factory {
public:// 重写基类的纯虚函数 create,创建香蕉对象std::shared_ptr<Fruit> create() override {return std::make_shared<Banana>();}
};int main() {// 创建一个指向 AppleFactory 的智能指针std::shared_ptr<Factory> fruit_factory(new AppleFactory());// 调用工厂的 create 方法创建苹果对象std::shared_ptr<Fruit> fruit = fruit_factory->create();// 调用水果对象的 name 方法输出名称fruit->name();// 重置工厂指针,指向 BananaFactoryfruit_factory.reset(new BananaFactory());// 调用新工厂的 create 方法创建香蕉对象fruit = fruit_factory->create();// 调用水果对象的 name 方法输出名称fruit->name();return 0;
}

优缺点

  • 优点:符合开闭原则,当需要新增产品时,只需要新增具体产品类和对应的具体工厂类,不需要修改现有代码。
  • 缺点:类的数量会增多,增加了系统的复杂度。

三、抽象工厂模式

概念

抽象工厂模式:工厂方法模式通过引入工厂等级结构,解决了简单工厂模式中工厂类职责太重的问题。但由于工厂方法模式中的每个工厂只生产一类产品,可能会导致系统中存在大量的工厂类,势必会增加系统的开销。此时,我们可以考虑将一些相关的产品组成一个产品族(位于不同产品等级结构中功能相关联的产品组成的家族),由同一个工厂来统一生产,这就是抽象工厂模式的基本思想。

实现步骤

  1. 定义产品族的抽象基类:为每个产品族定义一个抽象基类。
  2. 创建具体产品类:实现每个产品族的具体产品类。
  3. 定义抽象工厂类:定义一个抽象的工厂类,其中包含多个创建不同产品的抽象方法。
  4. 创建具体工厂类:继承抽象工厂类,实现创建不同产品的方法,决定创建哪些具体产品。

示例代码

#include<iostream>
#include<memory>// 水果抽象基类,定义了输出水果名称的纯虚函数
class Fruit {
public:virtual void name() = 0;
};// 苹果类,继承自 Fruit 类,实现了输出苹果名称的方法
class Apple : public Fruit {
public:void name() override {std::cout << "Apple" << std::endl;}
};// 香蕉类,继承自 Fruit 类,实现了输出香蕉名称的方法
class Banana : public Fruit {
public:void name() override {std::cout << "Banana" << std::endl;}
};// 动物抽象基类,定义了输出动物名称的纯虚函数
class Animal {
public:virtual void name() = 0;
};// 羊类,继承自 Animal 类,实现输出名称方法
class Lamb : public Animal {
public:void name() override {std::cout << "Lamb" << std::endl;}
};// 狗类,继承自 Animal 类,实现了输出狗名称的方法
class Dog : public Animal {
public:void name() override {std::cout << "Dog" << std::endl;}
};// 抽象工厂类,定义了获取水果和动物对象的纯虚函数
class Factory {
public:virtual std::shared_ptr<Fruit> getFruit(const std::string& name) = 0;virtual std::shared_ptr<Animal> getAnimal(const std::string& name) = 0;
};// 水果工厂类,继承自 Factory 类,实现了获取水果对象的方法,获取动物对象返回空指针
class FruitFactory : public Factory {
public:virtual std::shared_ptr<Fruit> getFruit(const std::string& name);virtual std::shared_ptr<Animal> getAnimal(const std::string& name);
};// 动物工厂类,继承自 Factory 类,实现了获取动物对象的方法,获取水果对象返回空指针
class AnimalFactory : public Factory {
public:virtual std::shared_ptr<Fruit> getFruit(const std::string& name);virtual std::shared_ptr<Animal> getAnimal(const std::string& name);
};// 工厂管理类,提供静态方法根据名称创建对应的工厂对象
class FactoryManager {
public:static std::shared_ptr<Factory> creaete(const std::string& name);
};int main() {// 通过工厂管理类创建水果工厂对象std::shared_ptr<Factory> fruit_factory = FactoryManager::creaete("fruit");// 从水果工厂获取苹果对象并输出名称std::shared_ptr<Fruit> fruit = fruit_factory->getFruit("apple");fruit->name();// 从水果工厂获取香蕉对象并输出名称fruit = fruit_factory->getFruit("banana");fruit->name();return 0;
}

优缺点

  • 优点:将一系列相关的产品对象的创建封装在一起,保证了产品之间的一致性,同时也符合开闭原则。
  • 缺点:实现复杂,当产品族需要增加新的产品时,需要修改抽象工厂类和所有具体工厂类的代码。

相关文章:

设计模式 之 工厂模式(简单工厂模式、工厂方法模式、抽象工厂模式)(C++)

文章目录 C 工厂模式引言一、简单工厂模式概念实现步骤示例代码优缺点 二、工厂方法模式概念实现步骤示例代码优缺点 三、抽象工厂模式概念实现步骤示例代码优缺点 C 工厂模式 引言 在 C 编程中&#xff0c;对象的创建是一个常见且基础的操作。然而&#xff0c;当项目规模逐渐…...

Windows前端开发IDE选型全攻略

Windows前端开发IDE选型全攻略 一、核心IDE对比矩阵 工具名称最新版本核心优势适用场景推荐指数引用来源VS Code2.3.5轻量级/海量插件/跨平台/Git深度集成全栈开发/中小型项目⭐⭐⭐⭐⭐14WebStorm2025.1智能提示/框架深度支持/企业级调试工具大型项目/专业前端团队⭐⭐⭐⭐47…...

大模型训练中的数据不平衡问题及其解决策略

目录 大模型训练中的数据不平衡问题及其解决策略 一、数据不平衡问题的影响 二、处理数据不平衡问题的方法 1. 过采样&#xff08;Oversampling&#xff09; 2. 欠采样&#xff08;Undersampling&#xff09; 3. 代价敏感学习&#xff08;Cost-Sensitive Learning&#xf…...

Flask应用实战经验总结:使用工厂函数创建app与uWSGI服务部署启动失败解决方案

在 Flask 应用开发中&#xff0c;使用工厂函数创建应用实例&#xff0c;并借助 uWSGI 服务进行部署&#xff0c;是常见且高效的组合。 然而&#xff0c;在实际操作过程中&#xff0c;uWSGI 配置文件与应用启动函数之间的关系复杂&#xff0c;容易引发各种问题。 本文将详细探…...

基于Spring Boot的党员学习交流平台设计与实现(LW+源码+讲解)

专注于大学生项目实战开发,讲解,毕业答疑辅导&#xff0c;欢迎高校老师/同行前辈交流合作✌。 技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;…...

TCP/IP的分层结构、各层的典型协议,以及与ISO七层模型的差别

1. TCP/IP的分层结构 TCP/IP模型是一个四层模型&#xff0c;主要用于网络通信的设计和实现。它的分层结构如下&#xff1a; (1) 应用层&#xff08;Application Layer&#xff09; 功能&#xff1a;提供应用程序之间的通信服务&#xff0c;处理特定的应用细节。 典型协议&am…...

【2025-02-25】基础算法:二分查找(一)

&#x1f4dd;前言说明&#xff1a; ●本专栏主要记录本人的基础算法学习以及LeetCode刷题记录&#xff0c;主要跟随B站博主灵茶山的视频进行学习&#xff0c;专栏中的每一篇文章对应B站博主灵茶山的一个视频 ●题目主要为B站视频内涉及的题目以及B站视频中提到的“课后作业”。…...

WebRTC解析

一、WebRTC 协议概述 WebRTC&#xff08;Web Real-Time Communication&#xff09;是由 Google 发起并成为 W3C 标准的实时音视频通信技术&#xff0c;核心特点&#xff1a; 零插件&#xff1a;浏览器原生支持端到端加密&#xff08;SRTP DTLS&#xff09;P2P 优先架构&…...

BERT模型详解及代码复现

架构设计 BERT模型的架构设计是其成功的关键之一,它巧妙地融合了Transformer架构的优势,并针对自然语言处理任务进行了优化。具体来说,BERT的架构主要由三个模块组成: Embedding模块 :负责将输入的文本转换为模型可处理的向量表示。该模块由三种Embedding组成: Token Em…...

如何在 SpringBoot 项目使用 Redis 的 Pipeline 功能

本文是博主在批量存储聊天中用户状态和登陆信息到 Redis 缓存中时&#xff0c;使用到了 Pipeline 功能&#xff0c;并对此做出了整理。 一、Redis Pipeline 是什么 Redis 的 Pipeline 功能可以显著提升 Redis 操作的性能&#xff0c;性能提升的原因在于可以批量执行命令。当我…...

Python Django系列—入门实例

我们假定你已经阅读了​ 安装 Django。你能知道 Django 已被安装&#xff0c;且安装的是哪个版本&#xff0c;通过在命令提示行输入命令&#xff08;由 $ 前缀&#xff09;。 $ python -m django --version 如果这行命令输出了一个版本号&#xff0c;证明你已经安装了此版本的…...

2024年第十五届蓝桥杯青少 图形化编程(Scratch)省赛中级组真题——截取递增数

截取递增数 背景信息 递增数&#xff1a;如果一个大于9的正整数各个数位上的数&#xff0c;从左到右是逐渐变大的&#xff0c;那么就称这个数为递增数。 例如124、248 是递增数。 给你一个不含0的九位数&#xff0c;请找出从这个九位数中能截取出的所有递增数。例如:115367…...

【ECMAScript6】

【ECMAScript6】 01. ES6介绍02. let和const命令03. 模板字符串04. 函数之默认值、剩余参数05. 函数之扩展运算符、箭头函数06. 箭头函数this指向和注意事项07. 解构赋值08. 扩展的对象的功能&#xff08;简写&#xff09;09. Symbol类型10. Set集合数据类型11. Map数据类型12.…...

WebUI 部署 Ollama 可视化对话界面

文章目录 一、Node.js 安装1.系统环境查询2.官网下载nodejs 安装包3.安装 Node.js 并配置环境变量4.验证安装是否正确 二、ollama-webui 安装与配置1.代码库下载2.依赖安装3.运行 三、遇到问题与解决 一、Node.js 安装 1.系统环境查询 ubuntu20.04 系统&#xff0c;x86-64架构…...

BMS应用软件开发 — 17 上下电控制与诊断开发 (Simulink)

目录 17.1 上下电控制流程 17.1.1 上下电流程 17.1.2 下电过程的电机放电 17.1.3 继电器状态检测 17.2 预充继电器状态判断 17.1 上下电控制流程 17.1.1 上下电流程 高压上电是指动力电池为车辆提供高压&#xff0c;使高压回路导通&#xff0c;为车辆的各个高压部件供电&…...

UE5 Gameplay框架及继承关系详解

文章目录 前言一、核心类及其继承关系二、核心类的职责与协作2.1 Actor & Pawn2.2 Controller2.3 GameMode & GameState2.4 PlayerState2.5 HUD & UI 三、协作流程示例总结 前言 Unreal Engine 5&#xff08;UE5&#xff09;的 Gameplay 框架 是一个高度模块化的系…...

html - 手工添加上次阅读的位置, 方便下次阅读

文章目录 html - 手工添加上次阅读的位置, 方便下次阅读概述笔记END html - 手工添加上次阅读的位置, 方便下次阅读 概述 在看一本电子书&#xff0c;有pdf格式的&#xff0c;但是比较喜欢看html格式的(复制比较方便)。 但是有个缺点&#xff0c;如果看到一半&#xff0c;关掉…...

JavaSE学习笔记26-集合(Collection)

集合 Java 中的集合&#xff08;Collection&#xff09;是 Java 标准库中非常重要的一部分&#xff0c;用于存储和操作一组对象。Java 集合框架&#xff08;Java Collections Framework&#xff09;提供了一套丰富的接口和类&#xff0c;用于处理各种数据结构&#xff0c;如列…...

使用Open WebUI下载的模型文件(Model)默认存放在哪里?

&#x1f3e1;作者主页&#xff1a;点击&#xff01; &#x1f916;Ollama部署LLM专栏&#xff1a;点击&#xff01; ⏰️创作时间&#xff1a;2025年2月21日21点21分 &#x1f004;️文章质量&#xff1a;95分 文章目录 使用CMD安装存放位置 默认存放路径 Open WebUI下…...

Django数据库操作

1、ORM 创建、删除、修改数据库的表中的数据&#xff0c;但不能创建数据库往数据库表中写入数据 表名&#xff1a;app名称_类名的小写 2、操作表数据 from django.db import modelsclass Department(models.Model):title models.CharField(verbose_name"部门", …...

005:Cesium.viewer 知识详解、示例代码

查看本专栏目录 - 本文是第 005个API内容详解 vue+cesium 示例教程200+目录 文章目录 一、Cesium.Viewer 知识详解1. 主要用途2. 构造函数与参数3. 常用属性(1)`viewer.scene`(2)`viewer.camera`(3)`viewer.entities`(4)`viewer.clock`4. 常用方法(1)`viewer.zoomTo(…...

蓝桥杯单片机组第十二届省赛第二批次

前言 第十二届省赛涉及知识点&#xff1a;NE555频率数据读取&#xff0c;NE555频率转换周期&#xff0c;PCF8591同时测量光敏电阻和电位器的电压、按键长短按判断。 本试题涉及模块较少&#xff0c;题目不难&#xff0c;基本上准备充分的都能完整的实现每一个功能&#xff0c;并…...

AI客服-接入deepseek大模型到微信(本地部署deepseek集成微信自动收发消息)

1.本地部署 1.1 ollama Ollama软件通过其高度优化的推理引擎和先进的内存管理机制&#xff0c;显著提升了大型语言模型在本地设备上的运行效率。其核心采用了量化技术&#xff08;Quantization&#xff09;以降低模型的计算复杂度和存储需求&#xff0c;同时结合张量并行计算&…...

Git 常用指令及其说明

配置相关 # 配置全局用户名 git config --global user.name "YourUsername"# 配置全局邮箱 git config --global user.email "your.emailexample.com"说明&#xff1a;这两条命令用于设置 Git 全局的用户名和邮箱&#xff0c;在提交代码时&#xff0c;这些…...

华为2025年技术发布会:智能汽车核心技术大爆发

近日&#xff0c;华为在鸿蒙智行尊界技术发布会上发布了多项智能汽车核心技术&#xff0c;涵盖智能驾驶、安全防护、通信系统、座舱交互及电池技术等领域&#xff0c;标志着其从“被动智能”向“自主智能”的战略升级。 以下是核心技术的综合梳理&#xff1a; 六大核心创新 途…...

SeaCMS V9海洋影视管理系统报错注入

漏洞背景 SQL 注入攻击是当前网络安全中最常见的一种攻击方式&#xff0c;攻击者可以利用该漏洞访问或操作数据库&#xff0c;造成数据泄露或破坏。通常发生在开发人员未能正确处理用户输入时。 在 SeaCMS V9 中&#xff0c;用户输入&#xff08;如登录、评论、分页、ID 等&a…...

vue3父子组件props传值,defineprops怎么用?(组合式)

目录 1.基础用法 2.使用解构赋值的方式定义props 3.使用toRefs的方式解构props (1).通过ref响应式变量&#xff0c;修改对象本身不会触发响应式 1.基础用法 父组件通过在子组件上绑定子组件中定义的props&#xff08;:props“”&#xff09;传递数据给子组件 <!-- 父组件…...

Django-Vue 学习-VUE

主组件中有多个Vue组件 是指在Vue.js框架中&#xff0c;主组件是一个父组件&#xff0c;它包含了多个子组件&#xff08;Vue组件&#xff09;。这种组件嵌套的方式可以用于构建复杂的前端应用程序&#xff0c;通过拆分功能和视图&#xff0c;使代码更加模块化、可复用和易于维…...

Ollama部署本地大模型DeepSeek-R1-Distill-Llama-70B

文章目录 一、下模二、转模1. 下载转换工具2. 安装环境依赖3. llama.cpp1. 转换脚本依赖2. llama.cpp安装依赖包3. llama.cpp编译安装4. 格式转换 三、Ollama部署1. 安装启动Ollama2. 添加模型3. 测试运行 一、下模 #模型下载 from modelscope import snapshot_download model…...

Zabbix问题记录2--踩坑HttpRequest,header添加无效

背景 在试图尝试通过Zabbix接入DeepSeek API的时候&#xff0c;由于使用了HTTP的方式&#xff0c;所以需要使用Zabbix 自带的HttpRequest库进行请求&#xff0c;产生了下面的问题 问题 curl curl -X POST https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completio…...