C++设计模式:工厂方法模式

devtools/2024/11/20 20:31:33/

工厂方法模式是一种创建型设计模式,其核心是将对象的创建延迟到子类中,通过定义一个接口来创建对象,使得子类决定实例化哪一个类。它在需要扩展产品类型时特别有用,能够避免代码的重复和耦合。


工厂方法模式的核心概念

  1. 抽象产品(Product)
    定义产品的接口,描述产品的公共行为。
  2. 具体产品(ConcreteProduct)
    实现抽象产品接口,定义具体的产品功能。
  3. 抽象工厂(Creator/Factory)
    定义一个工厂方法接口,用于创建产品对象。
  4. 具体工厂(ConcreteCreator/ConcreteFactory)
    实现工厂方法,创建特定类型的产品对象。
  5. 客户端(Client)
    通过工厂方法获取具体产品实例,而无需关心具体产品的实现细节。

案例背景:文档生成器

假设一个文档处理系统需要根据用户需求生成不同类型的文档(如 Word 文档和 PDF 文档)。我们希望通过工厂方法模式实现以下目标:

  1. 灵活扩展:添加新文档类型时,无需修改现有代码。
  2. 解耦逻辑:客户端代码不直接依赖具体文档类,而是通过工厂方法动态创建。

代码实现:文档生成器
#include <iostream>
#include <memory>
#include <string>// 抽象产品类:定义文档接口
class Document {
public:virtual void open() const = 0;  // 打开文档的接口方法virtual ~Document() = default;
};// 具体产品类:Word文档
class WordDocument : public Document {
public:void open() const override {std::cout << "打开Word文档。\n";}
};// 具体产品类:PDF文档
class PDFDocument : public Document {
public:void open() const override {std::cout << "打开PDF文档。\n";}
};// 抽象工厂类:定义文档工厂接口
class DocumentFactory {
public:virtual std::unique_ptr<Document> createDocument() const = 0;  // 工厂方法virtual ~DocumentFactory() = default;
};// 具体工厂类:Word文档工厂
class WordDocumentFactory : public DocumentFactory {
public:std::unique_ptr<Document> createDocument() const override {return std::make_unique<WordDocument>();  // 创建具体的Word文档对象}
};// 具体工厂类:PDF文档工厂
class PDFDocumentFactory : public DocumentFactory {
public:std::unique_ptr<Document> createDocument() const override {return std::make_unique<PDFDocument>();  // 创建具体的PDF文档对象}
};// 客户端代码:通过工厂方法创建文档
void clientCode(const DocumentFactory& factory) {// 工厂方法生成文档实例auto document = factory.createDocument();document->open();  // 使用文档对象
}int main() {std::cout << "客户端使用Word文档工厂:\n";WordDocumentFactory wordFactory;clientCode(wordFactory);std::cout << "\n客户端使用PDF文档工厂:\n";PDFDocumentFactory pdfFactory;clientCode(pdfFactory);return 0;
}

代码解析
  1. 抽象产品(Document)
    提供一个通用接口 open,定义所有文档的公共行为。

    class Document {
    public:virtual void open() const = 0;virtual ~Document() = default;
    };
    
  2. 具体产品(WordDocument 和 PDFDocument)
    分别实现 Document 接口,定义具体文档的行为。

    class WordDocument : public Document {
    public:void open() const override {std::cout << "打开Word文档。\n";}
    };
    
  3. 抽象工厂(DocumentFactory)
    定义一个接口 createDocument,负责创建文档对象。

    class DocumentFactory {
    public:virtual std::unique_ptr<Document> createDocument() const = 0;virtual ~DocumentFactory() = default;
    };
    
  4. 具体工厂(WordDocumentFactory 和 PDFDocumentFactory)
    分别实现抽象工厂接口,负责创建对应的文档对象。

    class WordDocumentFactory : public DocumentFactory {
    public:std::unique_ptr<Document> createDocument() const override {return std::make_unique<WordDocument>();}
    };
    
  5. 客户端代码
    客户端通过工厂方法获取具体的文档对象,完全解耦了具体产品的实现。

    void clientCode(const DocumentFactory& factory) {auto document = factory.createDocument();document->open();
    }
    

运行结果

客户端使用Word文档工厂:
打开Word文档。客户端使用PDF文档工厂:
打开PDF文档。

优缺点分析

优点
  1. 解耦对象创建与使用:客户端无需关心对象如何创建,专注于使用。
  2. 扩展性强:新增产品类型时,只需添加新的具体工厂类和产品类,无需修改现有代码。
  3. 符合单一职责原则:将对象的创建逻辑独立到工厂类中,便于维护。
缺点
  1. 类数量增加:每新增一个产品类型,就需要增加一个具体工厂类,可能导致类数量膨胀。
  2. 复杂度增加:相比直接实例化对象,工厂方法模式引入了更多的抽象和类层次。

适用场景

  1. 系统需要灵活创建对象:如根据不同条件动态创建不同类型的对象。
  2. 对象创建逻辑复杂:如需要对创建过程进行封装,避免在客户端暴露创建细节。
  3. 需要扩展性:系统需要频繁增加新对象类型。

改进方向

结合C++现代特性(如 std::unique_ptrstd::make_unique),可以使代码更加安全、简洁,并减少内存管理的复杂性。同时,可以将工厂与配置文件、命令行参数等结合,进一步提升灵活性。


总结

工厂方法模式是一个经典的创建型设计模式,它通过定义工厂接口和具体工厂,将对象的创建逻辑与使用逻辑分离,使得系统具有更好的扩展性和灵活性。在C++中,通过利用抽象类、继承和智能指针,可以高效实现工厂方法模式,并确保资源管理的安全性和代码的可维护性。


http://www.ppmy.cn/devtools/135575.html

相关文章

【MYSQL】什么是关系型数据库与非关系型数据库?

真正的让你快速理解什么是关系型数据库与非关系型数据库~ 主要是以查询语句&#xff0c;存储结构&#xff0c;拓展 性上的区别。 关系型数据库&#xff08;最经典就是mysql&#xff0c;oracle&#xff09;&#xff1a;它是支持SQL语言&#xff0c;并且关系型数据库大部分都支持…

强化学习数学原理学习(四)

前言 今天是时序差分学习 正文 首先,明确一点,时序差分也是无模型的情况下的强化学习方法,TD学习是蒙特卡洛思想和动态编程&#xff08;DP&#xff09;思想的结合。最基础的时序差分学习估计状态值&#xff0c;而后续提出的Sarsa和Q-learning方法则直接对动作值进行估计。 …

在Linux上如何利用NTP使客户端和服务端的时间同步

对于服务端 一、先在服务端安装相关配置-----yum install chrony -y-----并启动 二、进入chrony的文件里----在第三行修改为阿里云时间服务地址 三、在服务端重启chrony 四、进行测试------chronyc sources -v 五、进入chrony的文件里添加客户端的ip地址---在第26行&#…

关于adb shell登录开发板后terminal显示不完整

现象 今天有个同事跟我说&#xff0c;adb shell 登录开发板后&#xff0c;终端显示不完整&#xff0c;超出边界后就会出现奇怪的问题&#xff0c;比如字符覆盖显示等。如下图所示。 正常情况下应该如下图所示&#xff1a; 很明显&#xff0c;第一张图的显示区域只有完整区域…

爬虫策略——反爬机制

现代网站通常会使用多种反爬手段来限制爬虫访问数据。了解这些机制并针对性地制定绕过策略&#xff0c;是构建高效爬虫的关键。 1. 常见反爬手段 1.1 User-Agent 检查 网站通常会通过检查请求中的 User-Agent 字段&#xff0c;判断访问是否来自真实用户。爬虫默认的请求库&am…

ubuntu 22.04 shell

原因&#xff1a;在ubuntu&#xff08;18.04&#xff09;默认是指向bin/dash解释器的&#xff0c;dash是小巧的shell&#xff08;阉割版的bash&#xff09;&#xff0c;其功能远没有bash强大和丰富。上述问题就是dash不支持let和i运算等功能造成的。 ls -la /bin/sh lrwxrwxrw…

LeetCode 3239.最少翻转次数使二进制矩阵回文 I:遍历(行和列两种情况分别讨论)

【LetMeFly】3239.最少翻转次数使二进制矩阵回文 I&#xff1a;遍历&#xff08;行和列两种情况分别讨论&#xff09; 力扣题目链接&#xff1a;https://leetcode.cn/problems/minimum-number-of-flips-to-make-binary-grid-palindromic-i/ 给你一个 m x n 的二进制矩阵 grid…

NotePad++中安装XML Tools插件

一、概述 作为开发人员&#xff0c;日常开发中大部的数据是标准的json格式&#xff0c;但是对于一些古老的应用&#xff0c;例如webservice接口&#xff0c;由于其响应结果是xml&#xff0c;那么我们拿到xml格式的数据后&#xff0c;常常会对其进行格式化&#xff0c;以便阅读。…