【第一节】C++设计模式(创建型模式)-工厂模式

目录

前言

一、面向对象的两类对象创建问题

二、解决问题

三、工厂模式代码示例

四、工厂模式的核心功能

五、工厂模式的应用场景

六、工厂模式的实现与结构

七、工厂模式的优缺点

八、工厂模式的扩展与优化

九、总结


前言

        在面向对象系统设计中,开发者常常面临两类典型的对象创建问题。为了解决这些问题,工厂模式(Factory Pattern)应运而生,成为了一种广泛应用的解决方案。本文将详细探讨这两类问题,并分析工厂模式的功能、实现及其在实际开发中的应用。

一、面向对象的两类对象创建问题

(1)抽象基类与多态带来的问题

        为了提高代码的内聚性(Cohesion)和降低耦合性(Coupling),我们通常会抽象出类的公共接口,形成抽象基类或接口。通过声明指向基类的指针来指向实际子类的实现,从而实现多态性。然而,这种做法也带来了以下问题:
        子类名称依赖:客户端代码必须知道具体子类的名称。随着系统复杂度的增加,命名冲突和可读性问题变得难以处理,尤其是当开发者有不同的命名偏好时。
        扩展性与维护困难:每次使用子类时都需要显式地实例化(如 `new ×××`),导致代码重复,扩展性和维护性变差。

(2)父类无法确定具体子类的问题

        在某些情况下,父类并不知道具体要实例化哪一个子类。例如,假设在类 A 中需要使用类 B,而 B 是一个抽象父类。在类 A 中无法确定具体实例化哪一个 B 的子类,但类 A 的子类 D 可以知道。此时,在类 A 中无法直接使用类似 `new ×××` 的语句,因为具体类型未知。

二、解决问题

        我们通常使用工厂模式(Factory Pattern)来解决上述两个问题。在处理第一个问题时,常见的做法是声明一个创建对象的接口,并将对象的创建过程封装起来。此时,工厂类就像一个真正的“生产工厂”,负责生成所需的对象。

        而在第二个问题中,我们需要提供一个对象创建的接口,并在子类中实现具体的创建逻辑,因为只有子类能够决定实例化哪个具体类。 

第一中情况的 Factory 的结构示意图为:

图1

        图1展示了第一种情况的工厂模式结构示意图。

        这种模式在系统开发中经常被使用,但这并不是工厂模式的最大优势所在(因为这个问题可以通过其他方式解决)。工厂模式不仅仅提供了创建对象的接口,更重要的是它延迟了子类的实例化(即第二个问题)。以下是这种情况的工厂模式结构示意图: 

图2

        图2展示了第二种情况的工厂模式结构示意图。图2中的关键在于,工厂模式的应用并不仅限于封装对象的创建,而是将对象的创建过程放到子类中实现:工厂类只提供对象创建的接口,而具体的实现则由其子类(如 `ConcreteFactory`)完成。这正是图2与图1的主要区别所在。

三、工厂模式代码示例


Product.h

#ifndef PRODUCT_H
#define PRODUCT_H#include <iostream>// 抽象基类 Product
class Product {
public:virtual ~Product() = 0; // 纯虚析构函数
protected:Product() = default; // 默认构造函数,限制为派生类访问
};// 具体派生类 ConcreteProduct
class ConcreteProduct : public Product {
public:ConcreteProduct();~ConcreteProduct() override;
};#endif // PRODUCT_H


Product.cpp

#include "Product.h"// 纯虚析构函数的实现
Product::~Product() = default;// ConcreteProduct 实现
ConcreteProduct::ConcreteProduct() {std::cout << "ConcreteProduct created." << std::endl;
}ConcreteProduct::~ConcreteProduct() {std::cout << "ConcreteProduct destroyed." << std::endl;
}


Factory.h

#ifndef FACTORY_H
#define FACTORY_Hclass Product;// 抽象基类 Factory
class Factory {
public:virtual ~Factory() = 0; // 纯虚析构函数virtual Product* CreateProduct() = 0; // 工厂方法
protected:Factory() = default; // 默认构造函数,限制为派生类访问
};// 具体派生类 ConcreteFactory
class ConcreteFactory : public Factory {
public:ConcreteFactory();~ConcreteFactory() override;Product* CreateProduct() override;
};#endif // FACTORY_H


Factory.cpp

#include "Factory.h"
#include "Product.h"
#include <iostream>// 纯虚析构函数的实现
Factory::~Factory() = default;// ConcreteFactory 实现
ConcreteFactory::ConcreteFactory() {std::cout << "ConcreteFactory created." << std::endl;
}ConcreteFactory::~ConcreteFactory() {std::cout << "ConcreteFactory destroyed." << std::endl;
}Product* ConcreteFactory::CreateProduct() {return new ConcreteProduct();
}


main.cpp

#include "Factory.h"
#include "Product.h"
#include <iostream>int main() {// 创建工厂对象Factory* fac = new ConcreteFactory();// 使用工厂创建产品对象Product* p = fac->CreateProduct();// 释放对象delete p;delete fac;return 0;
}

        在示例代码中,工厂模式(Factory Pattern)被用来解决父类无法确定具体要实例化哪一个子类的问题。至于为创建对象提供接口的问题,可以通过在工厂类中附加相应的创建操作来实现,例如添加 `Create***Product()` 方法。

        工厂模式(Factory Pattern)在实际开发中应用非常广泛,尤其是在面向对象系统中,开发者经常面临对象创建的问题:需要创建的类数量非常多。工厂模式通过提供创建对象的接口封装(第一个功能)以及将类的实例化推迟到子类(第二个功能),部分地解决了这些实际问题。一个典型的例子是笔者在开发 VisualCMCS 系统的语义分析模块时,由于需要为文法中的每个非终结符构造一个处理类,因此对象的创建非常频繁。采用工厂模式后,系统的可读性和维护性都变得更加优雅(elegant)。

        然而,工厂模式也带来至少以下两个问题:  
        (1)接口封闭性问题:如果为每一个具体的 `ConcreteProduct` 类的实例化提供一个单独的函数体,那么我们可能不得不在系统中不断添加新的方法来处理这些新创建的 `ConcreteProduct`。这样,工厂接口就难以做到封闭(Close)。虽然可以通过创建一个工厂的子类来利用多态性实现这一点,但这也会导致需要新增一个类作为代价。  
        (2)参数化工厂方法:在实现中,我们可以通过参数化工厂方法,即给 `FactoryMethod()` 传递一个参数来决定创建哪一个具体的 `Product`(实际上,笔者在 VisualCMCS 中也采用了这种方式)。此外,还可以通过模板化来避免第一个问题中的子类创建,具体方法是将具体的 `Product` 类作为模板参数,实现起来也非常简单。  

        可以看出,工厂模式为对象的创建提供了一种优秀的实现策略。然而,工厂模式仅限于处理同一类别的类(即这些类有一个共同的基类)。如果我们需要为不同类别的类提供一个对象创建的接口,那么就需要使用抽象工厂模式(AbstractFactory)了。抽象工厂模式我们下节再讲。

四、工厂模式的核心功能

工厂模式通过以下两个核心功能,解决了上述问题:
(1)定义创建对象的接口,封装对象的创建过程:工厂模式将对象的创建过程抽象化,客户端代码只需依赖工厂接口,而无需关心具体类的实例化细节。
(2)将具体类的实例化延迟到子类:工厂模式允许子类决定实例化哪个具体类,从而将对象创建的决策推迟到运行时。

五、工厂模式的应用场景

(1)封装对象创建
        在第一个问题中,工厂模式通过声明一个创建对象的接口,封装了对象的创建过程。工厂类类似于一个“生产对象”的工厂,客户端代码只需调用工厂接口,而无需直接依赖具体类。

(2)延迟实例化到子类
        在第二个问题中,工厂模式将具体类的实例化延迟到子类。父类只需定义创建对象的接口,而具体实现则由子类完成。这种方式不仅解决了父类无法确定具体子类的问题,还提高了代码的灵活性和可扩展性。

六、工厂模式的实现与结构

(1)简单工厂模式
        简单工厂模式通过一个工厂类封装对象的创建过程。客户端代码只需调用工厂类的方法,即可获取所需对象。然而,这种模式的扩展性较差,新增产品类型时需要修改工厂类。

(2)工厂方法模式
        工厂方法模式将对象的创建延迟到子类。抽象工厂类定义创建对象的接口,具体工厂类负责实例化具体产品。这种方式符合开闭原则,新增产品类型时只需添加新的工厂类,而无需修改现有代码。

(3)抽象工厂模式
        抽象工厂模式用于创建一系列相关或依赖的对象。它为不同产品族提供创建接口,而具体工厂类负责实例化特定产品族中的对象。这种方式适用于需要创建多个不同类型对象的场景。

七、工厂模式的优缺点

(1)优点
        解耦:将对象创建与使用分离,降低了代码的耦合性。
        扩展性:新增产品类型时无需修改现有代码,符合开闭原则。
        灵活性:通过多态将对象创建延迟到运行时,支持动态决策。

(2)缺点
        类数量增加:每新增一个产品类型,可能需要添加新的工厂类,导致类数量膨胀。
        复杂性增加:工厂模式的实现可能增加系统的复杂性,尤其是抽象工厂模式。

八、工厂模式的扩展与优化

(1)参数化工厂方法
        通过为工厂方法传递参数,决定具体创建哪一个产品。这种方式可以减少工厂类的数量,但可能增加工厂方法的复杂性。

(2)模板化工厂
        将具体产品类作为模板参数,避免为每个产品类型创建新的工厂类。这种方式在 C++ 等支持模板的语言中实现较为简单。

(3)抽象工厂模式
        当需要为不同类的产品提供创建接口时,可以使用抽象工厂模式。抽象工厂模式为每个产品族提供一个创建接口,适用于复杂对象创建的场景。

九、总结

        工厂模式是面向对象设计中解决对象创建问题的经典模式。它通过封装对象创建过程和延迟实例化到子类,有效地降低了代码的耦合性,提高了系统的扩展性和灵活性。尽管工厂模式可能增加类的数量和系统的复杂性,但其在解耦和支持变化方面的优势使其在实际开发中得到了广泛应用。对于需要频繁创建对象的系统,工厂模式无疑是一种优雅且高效的解决方案。

        参考学习书籍:设计模式精解-GoF 23 种设计模式解析

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/70254.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

DeepSeek:企业级大模型私有化部署与应用全解析(深度扩展版)

一、DeepSeek基本信息介绍(扩展) DeepSeek作为中国首个实现全栈自主可控的开源大模型体系,其技术演进经历了DeepSeek LLM→DeepSeek MoE→DeepSeek-V2→DeepSeek-V3→DeepSeek R1五大阶段。核心突破在于: 架构创新:在Transformer基础上引入分组查询注意力(GQA),推理成…

zyNo.25

SSRF漏洞 在了解ssrf漏洞前先了解curl命令的使用 1.curl命令的使用 基本格式&#xff1a;curl<参数值>请求地址 get请求&#xff1a;curl http://127.0.0.1 post请求&#xff1a;curl -X POST -d "a1&b2" http://127.0.0.1/(其中&#xff0c;使用-X参…

文件理解:从C标准库到系统调用

目录 一、C 标准库文件操作 1. 文件的写入 2. 文件的读取 3. 数据输出到显示器 4. 标准输入输出流 二、C 标准库文件操作模式 三、系统调用文件操作 1. 文件的打开与描述符 2. 文件的读取 3. 文件操作标志 4. 文件权限 5. 文件描述符 四、C 标准库与系统调用的比较…

【阮一峰】5.函数

函数 简介 函数的类型声明&#xff0c;需要在声明函数时&#xff0c;给出参数的类型和返回值的类型。 function hello(txt: string): void {console.log("hello " txt); }如果变量被赋值为一个函数&#xff0c;变量的类型有两种写法。 // 写法一 const hello f…

【R语言】主成分分析与因子分析

一、主成分分析 主成分分析&#xff08;Principal Component Analysis, PCA&#xff09;是一种常用的无监督数据降维技术&#xff0c;广泛应用于统计学、数据科学和机器学习等领域。它通过正交化线性变换将&#xff08;高维&#xff09;原始数据投影到一个新的坐标系&#xff…

pycharm画图程序如何一步一步的调试

1.设置合适的 Matplotlib 后端 在 PyCharm 中&#xff0c;有时需要手动指定 Matplotlib 后端。你可以尝试在脚本的最开始加入以下代码&#xff0c;强制使用 TkAgg 后端&#xff0c;这样可以保证图形更新的实时性&#xff1a; import matplotlib matplotlib.use(TkAgg) # 指定…

基于Java+Swing+Mysql实现旅游管理信息系统

基于JavaSwingMysql实现旅游管理信息系统 一、系统介绍二、功能展示1.登陆2.注册3.旅游信息查询4.查看游行团信息5.报名6、报名信息管理 三、数据库四、其它1.其他系统实现五.获取源码 一、系统介绍 用户&#xff1a;登陆、注册、旅游信息查询、查看游行团信息、报名 管理员&a…

Linux配置端口映射——其他机器可以访问

一般使用虚拟机都是NAT网络模式&#xff0c;但是这种模式的问题是&#xff1a;其他机器不能访问虚拟机 想让其他机器访问这个电脑上的虚拟机&#xff0c;需要做端口映射。 之后就可以使用finalshell连接 注意&#xff1a;如果要连接其他人的虚拟机&#xff0c;需要对方先关闭自…

快速部署deepseek

一、安装ollama 访问https://ollama.com/download 下载并安装对应系统的ollama。 Ollama 是一个开源工具&#xff0c;旨在帮助用户在本地机器上轻松运行和管理大型语言模型&#xff08;LLM&#xff09;。它提供了一个简单易用的命令行界面&#xff0c;可以下载、安装和运行各…

用Deepseek查询快证API-物流查询-实名认证-企业实名认证

快证API可能是一个提供多种验证和查询服务的平台&#xff0c;包括但不限于企业实名认证、短链接生成、手机号归属地查询、IP地址查询等。以下是根据搜索结果整理的关于快证API的相关信息&#xff1a; ‌企业实名认证API‌&#xff1a; 功能&#xff1a;通过与企业相关数据库进行…

基于指纹识别技术的考勤打卡设计与实现(论文+源码)

1 系统总体设计 本次基于指纹识别技术的考勤打卡系统的整体框图如图2.1所示&#xff0c;主控制模块选用单片机STC89C52&#xff0c;同时还包括AT24C02存储电路&#xff0c;指纹模块&#xff0c;LCD12864液晶&#xff0c;继电器&#xff0c;矩阵键盘等硬件电路。其中指纹模块和…

【云安全】云原生-K8S(四)安全问题分析

Kubernetes&#xff08;K8S&#xff09;因其强大的容器编排能力成为了云计算和微服务架构的首选&#xff0c;但同时也带来了复杂的安全挑战。本文将概述K8S的主要安全问题&#xff0c;帮助安全工程师理解潜在威胁&#xff0c;并采取相应的防护措施。 K8S 攻击面概览 下面两张…

基于JAVA毕业生信息招聘信息平台设计与实现

以往的毕业生信息招聘信息管理事务处理主要使用的是传统的人工管理方式&#xff0c;这种管理方式存在着管理效率低、操作流程繁琐、保密性差等缺点&#xff0c;长期的人工管理模式会产生大量的文本文件与文本数据&#xff0c;这对事务的查询、更新以及维护带来不少困难。随着互…

ES6模块化和CommonJs模块化区别

ES6模块化和CommonJs模块化区别 在JavaScript中&#xff0c;模块化是将代码拆分成独立的块&#xff0c;每个块可以独立封装和管理。ES6模块化和CommonJS是两种常见的模块化规范&#xff0c;它们在语法、加载方式和运行时特性上有显著差异。 语法差异 CommonJS模块使用requir…

DeepSeek自动化写作软件

DeepSeek写作软件的三大核心功能 对于内容创作者来说&#xff0c;写作不仅是表达思想的过程&#xff0c;更是一项需要投入大量时间和精力的任务。面对日益增长的内容需求&#xff0c;写作效率低下、内容质量不高等问题&#xff0c;常常让创作者感到焦虑。而 DeepSeek 写作软件…

深入解析 Flutter Bloc:从原理到实战

深入解析 Flutter Bloc&#xff1a;从原理到实战 Bloc&#xff08;Business Logic Component&#xff09;是 Flutter 中一个强大的状态管理工具&#xff0c;基于事件驱动的架构设计&#xff0c;适合管理复杂的业务逻辑和状态。Bloc 的核心理念是将业务逻辑与 UI 分离&#xff…

使用右侧值现象来处理一个word导入登记表的需求

需求也简单&#xff0c;导word文件用户登记表&#xff0c;有各部门的十几个版本&#xff08;为什么这么多&#xff1f;不知道&#xff09;。这里说下谈下我的一些代码做法&#xff1a; 需求分析&#xff1a; 如果能解决java字段和各项填的值怎么配对的问题&#xff0c;那么就…

Day48(补)【AI思考】-设计模式三大类型统一区分与记忆指南

文章目录 设计模式三大类型统一区分与记忆指南**一、创建型模式&#xff08;对象如何生&#xff1f;&#xff09;****二、结构型模式&#xff08;对象如何组&#xff1f;&#xff09;****三、行为型模式&#xff08;对象如何动&#xff1f;&#xff09;****1. 行为型类模式&…

Rook-ceph(1.92最新版)

安装前准备 #确认安装lvm2 yum install lvm2 -y #启用rbd模块 modprobe rbd cat > /etc/rc.sysinit << EOF #!/bin/bash for file in /etc/sysconfig/modules/*.modules do[ -x \$file ] && \$file done EOF cat > /etc/sysconfig/modules/rbd.modules &l…

Transformer技术报告:架构与原理

【深度学习】Transformer 技术报告&#xff1a;架构与原理 一、引言二、Transformer 的基本架构2.1 总体架构2.2 编码器&#xff08;Encoder&#xff09;2.3 解码器&#xff08;Decoder&#xff09;2.4 输入嵌入与位置编码 三、Transformer 的关键特性四、应用场景五、总结 一、…