C++版设计模式简介 与 初识 工厂模式

news2024/11/26 13:40:59

目录

    • 前言
  • 一、设计模式简介
    • 1. 什么是设计模式
    • 2. 设计模式分类
    • 3. 设计模式的优点
    • 4. 设计模式的实践
  • 二、单例模式
    • 1. 单例模式的关键点
    • 2. 单例模式的实现方式
      • 饿汉式单例(Eager Initialization)
      • 懒汉式单例(Lazy Initialization)
      • 双重检查锁定(Double-Checked Locking)
      • 登记式单例(Registry-based Singleton)
      • 枚举式单例(Enum-based Singleton)
    • 3. 单例模式的优缺点
    • 4. 示例程序
  • 三、工厂模式
    • 1. 简单工厂模式
      • 特点:
      • 示例代码:
      • 缺点:
    • 2. 工厂方法模式
      • 特点:
      • 示例代码:
    • 3. 抽象工厂模式
      • 特点:
      • 示例代码1:
      • 示例代码2:
    • 4. 使用场景
    • 5. 总结

前言

  关于并发编程的更多内容我会在后续的实际开发中进行总结,然后分享给大家!接下来将会和大家一起学习C++的设计模式😷

一、设计模式简介

1. 什么是设计模式

  (1)设计模式 是 软件开发人员在软件开发过程中 面临的 一般问题的解决方案。

  (2)这些解决方案是众多软件开发人员经过 相当长的一段时间的 试验 和 错误总结出来的。

  (3)使用设计模式是为了重用代码、让代码更容易被他人理解、保证代码可靠性。

2. 设计模式分类

  设计模式提供了一种标准化的方法来处理软件设计中的问题,使得代码更加灵活、可维护和可重用。它们通常是在特定的上下文中应用,并在软件开发的不同阶段(如设计、编码、测试等)起到指导作用。

  设计模式有23种左右。设计模式一般分为以下几类:

  1. 创建型模式(Creational Patterns):这些模式关注对象的创建过程,旨在将对象的创建与使用分离。常见的创建型模式包括(常用的是前三种):

    • 单例模式(Singleton Pattern):确保一个类只有一个实例,并提供全局访问点。
    • 工厂方法模式(Factory Method Pattern):定义一个用于创建对象的接口,但由子类决定要实例化的类。
    • 抽象工厂模式(Abstract Factory Pattern):提供一个创建一系列相关或互相依赖对象的接口,而无需指定具体类。
    • 建造者模式(Builder Pattern):通过将一个复杂对象的构建过程与它的表示分离,来构建一个复杂对象。
    • 原型模式(Prototype Pattern):通过复制现有的实例来创建新实例,而不是通过构造函数。
  2. 结构型模式(Structural Patterns):这些模式关注类和对象的组合,帮助确保系统中的组件可以组合成更复杂的结构。常见的结构型模式包括:

    • 适配器模式(Adapter Pattern):将一个类的接口转换成客户端所期望的另一种接口。
    • 桥接模式(Bridge Pattern):将抽象部分与它的实现部分分离,使它们可以独立变化。
    • 装饰器模式(Decorator Pattern):动态地给一个对象添加一些额外的职责。
    • 外观模式(Facade Pattern):为子系统中的一组接口提供一个一致的界面,使子系统更易使用。
    • 享元模式(Flyweight Pattern):通过共享大量细粒度的对象来有效地支持大量的对象。
    • 组合模式(Composite Pattern):将对象组合成树形结构以表示部分-整体层次结构。
  3. 行为型模式(Behavioral Patterns):这些模式关注对象之间的交互和职责分配。关注对象之间的通信,譬如将行为、数据、展示拆分又互相通信的MVC架构,经典MVC模式中,M是指业务模型,V是指用户界面,C则是控制器,使用MVC的目的是将M和V的实现代码分离,从而使同一个程序可以使用不同的表现形式。其中,View的定义比较清晰,就是用户界面。
    常见的行为型模式包括:

    • 策略模式(Strategy Pattern):定义一系列算法,将每一个算法封装起来,并使它们可以互相替换。
    • 观察者模式(Observer Pattern):定义对象间一对多的依赖关系,以便当一个对象的状态改变时,所有依赖于它的对象都会得到通知并自动更新。
    • 迭代器模式(Iterator Pattern):提供一种方法顺序访问一个集合对象中的各个元素,而不暴露该对象的内部表示。
    • 模板方法模式(Template Method Pattern):定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。
    • 状态模式(State Pattern):允许一个对象在其内部状态改变时改变它的行为。
    • 命令模式(Command Pattern):将请求封装为一个对象,从而使你可以用不同的请求对客户进行参数化、排队请求或记录请求日志。
    • 责任链模式(Chain of Responsibility Pattern):使多个对象有机会处理请求,从而避免请求发送者和接收者之间的耦合。

  框架、设计模式这两个概念总容易被混淆,其实它们之间还是有区别的。框架通常是代码重用,而设计模式是设计重用,架构则介于两者之间,部分代码重用,部分设计重用,有时分析也可重用。

  在软件生产中有三种级别的重用:(1)内部重用,即在同一应用中能公共使用的抽象块;(2)代码重用,即将通用模块组合成库或工具集,以便在多个应用和领域都能使用;(2)应用框架的重用,即为专用领域提供通用的或现成的基础结构,以获得最高级别的重用性。

3. 设计模式的优点

  • 提高代码复用:通过模式化的解决方案,可以在不同的项目中重复使用相同的设计。
  • 提高代码灵活性:设计模式使系统具有更好的扩展性和灵活性,便于在需求变化时进行调整。
  • 促进团队合作:标准化的设计模式可以让团队成员更容易理解和维护代码,提高团队的沟通效率。
  • 简化设计过程:通过提供通用的解决方案,设计模式可以帮助开发人员解决复杂的设计问题。
  • 开发人员的共同平台:提供了一个标准的术语系统,且具体到特定的情景。例如,单例设计模式意味着使用单个对象,这样所有熟悉单例设计模式的开发人员都能使用单个对象,并且可以通过这种方式告诉对方,程序使用的是单例模式。
  • 最佳的实践:设计模式已经经历了很长一段时间的发展,它们提供了软件开发过程中面临的一般问题的最佳解决方案。学习这些模式有助于经验不足的开发人员通过一种简单快捷的方式来学习软件设计。

4. 设计模式的实践

  • 学习经典书籍:如《设计模式:可复用面向对象软件的基础》(Erich Gamma 等著),这是设计模式的经典参考书。
  • 应用到实际项目中:在实际项目中尝试应用设计模式,逐步理解其优势和适用场景。
  • 分析开源项目:阅读和分析开源项目的设计,了解如何在实际中应用设计模式。

  分享下个人的一些资料:《23种设计模式(C++完整版).pdf》,来自于网络,链接如下

链接:https://pan.baidu.com/s/1M2A9S6WKJ3_7BZipYd1SzQ 
提取码:ch3q 
--来自百度网盘超级会员V6的分享

  程杰《大话设计模式》一书,使用JAVA编写示例程序,但是会C++的话,是可以看懂这些例程的,面向对象的这些语法二者很相似,我是买了一本二手书进行学习的。安利一个买正版二手书的微信小程序《多抓鱼》,不是广告,单纯觉得好用分享给大家
在这里插入图片描述

二、单例模式

  单例模式(Singleton Pattern)是一种设计模式,其主要目的是确保一个类只有一个实例,并提供全局访问点。它通常用于需要全局唯一实例的场景,比如数据库连接池、配置管理器等。

1. 单例模式的关键点

  • 唯一性: 确保类只有一个实例。
  • 全局访问: 提供全局访问点来获取该实例。
  • 延迟初始化: (可选)在第一次使用时才创建实例。

2. 单例模式的实现方式

  单例模式有多种实现方式,下面列出了几种常见的方法。

饿汉式单例(Eager Initialization)

  特点: 类加载时即创建实例,不管是否使用。简单且线程安全,但可能导致资源浪费。

class Singleton {
public:
    static Singleton* getInstance() {
        return instance;
    }

private:
    Singleton() {}
    static Singleton* instance;
};

// 类加载时即创建实例
Singleton* Singleton::instance = new Singleton();

懒汉式单例(Lazy Initialization)

  特点: 在第一次使用时创建实例。需要注意线程安全问题。

class Singleton {
public:
    static Singleton* getInstance() {
        if (instance == nullptr) {
            instance = new Singleton();
        }
        return instance;
    }

private:
    Singleton() {}
    static Singleton* instance;
};

// 类外定义静态成员
Singleton* Singleton::instance = nullptr;

双重检查锁定(Double-Checked Locking)

  特点: 在懒汉式基础上加锁,避免了多线程环境下的同步开销。

#include <mutex>

class Singleton {
public:
    static Singleton* getInstance() {
        if (instance == nullptr) {
            std::lock_guard<std::mutex> lock(mutex);
            if (instance == nullptr) {
                instance = new Singleton();
            }
        }
        return instance;
    }

private:
    Singleton() {}
    static Singleton* instance;
    static std::mutex mutex;
};

// 类外定义静态成员
Singleton* Singleton::instance = nullptr;
std::mutex Singleton::mutex;

登记式单例(Registry-based Singleton)

  特点: 使用静态成员注册实例,避免了静态初始化顺序问题,但有额外的复杂性。

#include <map>
#include <string>

class Singleton {
public:
    static Singleton* getInstance(const std::string& name) {
        if (instances.find(name) == instances.end()) {
            instances[name] = new Singleton();
        }
        return instances[name];
    }

private:
    Singleton() {}
    static std::map<std::string, Singleton*> instances;
};

// 类外定义静态成员
std::map<std::string, Singleton*> Singleton::instances;

枚举式单例(Enum-based Singleton)

  特点: 使用枚举类创建单例,线程安全且简单,但不支持延迟初始化。

#include <iostream>

class Singleton {
public:
    // 获取单例实例
    static Singleton& getInstance() {
        return instance;
    }

    // 示例方法
    void showMessage() const {
        std::cout << "Hello, Singleton!" << std::endl;
    }

private:
    // 构造函数是私有的,防止直接创建实例
    Singleton() {
        std::cout << "Singleton instance created." << std::endl;
    }

    // 析构函数是私有的,防止删除实例
    ~Singleton() {
        std::cout << "Singleton instance destroyed." << std::endl;
    }

    // 删除拷贝构造函数和赋值操作符
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;

    // 枚举成员,强制创建单例实例
    enum { InstanceID };
    static Singleton instance;
};

// 静态成员初始化
Singleton Singleton::instance;

int main() {
    // 获取单例实例
    Singleton& singleton1 = Singleton::getInstance();
    singleton1.showMessage();

    // 尝试获取另一个单例实例
    Singleton& singleton2 = Singleton::getInstance();
    singleton2.showMessage();

    // 检查两个实例是否相同
    if (&singleton1 == &singleton2) {
        std::cout << "Both instances are the same." << std::endl;
    } else {
        std::cout << "Instances are different." << std::endl;
    }

    return 0;
}

3. 单例模式的优缺点

优点:

  1. 节省资源: 确保只有一个实例,避免了多次创建。
  2. 全局访问: 提供了全局访问点,方便管理。

缺点:

  1. 难以测试: 单例模式会使得单元测试变得复杂,因为它在多个测试中共享状态。
  2. 线程安全问题: 在多线程环境中,需要特别注意线程安全。
  3. 违背单一职责原则: 单例类同时负责创建和管理自身实例,可能导致设计上的问题。

总的来说,单例模式在适当的场景下非常有效,但需要谨慎使用,特别是在多线程和测试方面。

4. 示例程序

  单例模式(Singleton Pattern)确保一个类只有一个实例,并提供一个全局访问点。以下是一个使用 C++ 实现单例模式的完美示例代码:

#include <iostream>
#include <mutex>

class Singleton {
public:
    // 删除拷贝构造函数和赋值操作符,以防止复制
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;

    // 获取 Singleton 实例的静态方法
    static Singleton& getInstance() {
        static Singleton instance; // 局部静态变量,保证线程安全
        return instance;
    }

    // 示例方法
    void showMessage() const {
        std::cout << "Hello, Singleton!" << std::endl;
    }

private:
    // 构造函数是私有的,防止直接创建实例
    Singleton() {
        std::cout << "Singleton instance created." << std::endl;
    }

    // 析构函数是私有的,防止删除实例
    ~Singleton() {
        std::cout << "Singleton instance destroyed." << std::endl;
    }
};

int main() {
    // 获取单例实例
    Singleton& singleton1 = Singleton::getInstance();
    singleton1.showMessage();

    // 尝试获取另一个单例实例
    Singleton& singleton2 = Singleton::getInstance();
    singleton2.showMessage();

    // 检查两个实例是否相同
    if (&singleton1 == &singleton2) {
        std::cout << "Both instances are the same." << std::endl;
    } else {
        std::cout << "Instances are different." << std::endl;
    }

    return 0;
}
  • 构造函数和析构函数:构造函数和析构函数是私有的,这样可以防止外部直接创建或销毁实例。
  • 静态方法 getInstance:这是获取单例实例的唯一方法。静态局部变量 instance 确保在程序生命周期内只有一个 Singleton 实例。这个静态变量会在第一次调用 getInstance时创建,并在程序结束时自动销毁。
  • 删除拷贝构造函数和赋值操作符:为了防止拷贝和赋值操作,我们将拷贝构造函数和赋值操作符声明为 delete
  • 线程安全:在 C++11 及以后的版本中,局部静态变量的初始化是线程安全的,因此我们不需要额外的同步机制。对于旧版本的 C++,可以使用互斥量来确保线程安全。

示例程序运行结果:

Singleton instance created.
Hello, Singleton!
Hello, Singleton!
Both instances are the same.

  这个示例展示了单例模式的核心要点,并确保了代码的线程安全性和实例唯一性。

三、工厂模式

  工厂模式是一种创建对象的设计模式,旨在通过提供一个创建对象的接口,使得子类可以决定实例化的类。工厂模式可以分为几个主要类型:简单工厂模式、工厂方法模式和抽象工厂模式。以下是对这些模式的详细介绍:

1. 简单工厂模式

  简单工厂模式通过一个工厂类负责创建不同类型的对象,客户端只需调用工厂方法,而不需要知道具体类的实现。

特点:

  • 封装对象创建:客户端只需了解工厂接口,而不需要了解具体的类。
  • 降低系统耦合度:客户端与具体产品解耦,便于维护和扩展。

示例代码:

  假设我们要创建一个简单的文档管理系统,其中有两种类型的文档:PDFDocumentWordDocument。我们可以使用简单工厂模式来创建这些文档。

#include <iostream>
#include <string>

//定义产品接口->定义工厂类->使用工厂类

//1、首先,我们定义一个文档接口 `Document`,以及两个具体的文档类 `PDFDocument` 和 `WordDocument`
// 产品接口
class Document {
public:
    virtual void open() const = 0; // 打开文档
    virtual ~Document() = default;
};

// 具体产品:PDF 文档
class PDFDocument : public Document {
public:
    void open() const override {
        std::cout << "Opening PDF document" << std::endl;
    }
};

// 具体产品:Word 文档
class WordDocument : public Document {
public:
    void open() const override {
        std::cout << "Opening Word document" << std::endl;
    }
};

//2、接着,我们定义一个工厂类 `DocumentFactory`,它负责根据类型创建不同的文档实例。
// 工厂类
class DocumentFactory {
public:
    // 工厂方法:根据文档类型创建相应的文档对象
    static Document* createDocument(const std::string& type) {
        if (type == "PDF") {
            return new PDFDocument();
        } else if (type == "Word") {
            return new WordDocument();
        } else {
            std::cerr << "Error: Unsupported document type" << std::endl;
            return nullptr;
        }
    }
};

//最后,我们在客户端代码中使用 `DocumentFactory` 来创建和操作文档对象。
int main() {
    // 创建 PDF 文档
    Document* pdfDoc = DocumentFactory::createDocument("PDF");
    if (pdfDoc) {
        pdfDoc->open();
        delete pdfDoc; // 记得释放内存
    }

    // 创建 Word 文档
    Document* wordDoc = DocumentFactory::createDocument("Word");
    if (wordDoc) {
        wordDoc->open();
        delete wordDoc; // 记得释放内存
    }

    // 尝试创建不支持的文档类型
    Document* unknownDoc = DocumentFactory::createDocument("Unknown");
    if (unknownDoc) {
        unknownDoc->open();
        delete unknownDoc; // 不会被调用,因为创建失败
    }

    return 0;
}
  • 产品接口 (Document): 定义了文档的基本操作 open(),确保所有具体产品都实现这个接口。
  • 具体产品 (PDFDocumentWordDocument): 实现了 Document 接口的具体文档类型。
  • 工厂类 (DocumentFactory): 提供静态方法 createDocument 来创建不同类型的文档实例,并根据类型选择具体的产品。
  • 客户端代码: 通过调用 DocumentFactory::createDocument 来获取所需的文档实例,并调用其方法。注意,客户端代码负责释放创建的文档对象以防内存泄漏。

  这个示例展示了如何使用简单工厂模式来管理对象的创建,隐藏了具体的类实现细节,使得客户端代码与具体的产品解耦。

缺点:

  • 违反开放-关闭原则:新增产品时需要修改工厂类。
  • 产品等级过多:工厂类可能变得庞大复杂。

2. 工厂方法模式

  工厂方法模式通过定义一个接口来创建对象,而将具体的创建过程延迟到子类中。每个子类负责自己的产品创建。

特点:

  • 遵循开放-关闭原则:可以通过增加新的工厂子类来支持新的产品,而不需修改现有代码。
  • 增加灵活性和扩展性:各个具体工厂之间的依赖较小。

示例代码:

  假设我们仍然使用文档管理系统的例子,但这次我们将使用工厂方法模式来创建文档。我们会定义一个工厂接口,每个具体的工厂子类负责创建特定类型的文档。

#include <iostream>
#include <string>

//定义产品接口->定义工厂接口->定义具体工厂->使用工厂方法模式

//1、首先,我们定义一个文档接口 Document,以及两个具体的文档类 PDFDocument 和 WordDocument。
// 产品接口
class Document {
public:
    virtual void open() const = 0; // 打开文档
    virtual ~Document() = default;
};

// 具体产品:PDF 文档
class PDFDocument : public Document {
public:
    void open() const override {
        std::cout << "Opening PDF document" << std::endl;
    }
};

// 具体产品:Word 文档
class WordDocument : public Document {
public:
    void open() const override {
        std::cout << "Opening Word document" << std::endl;
    }
};

//2、定义一个抽象的工厂接口 `DocumentFactory`,声明一个工厂方法 `createDocument`,用于创建
// `Document` 对象。
// 工厂接口
class DocumentFactory {
public:
    virtual Document* createDocument() const = 0; // 工厂方法
    virtual ~DocumentFactory() = default;
};

//3、实现具体的工厂类,每个工厂负责创建特定类型的文档。
// 具体工厂:PDF 文档工厂
class PDFDocumentFactory : public DocumentFactory {
public:
    Document* createDocument() const override {
        return new PDFDocument();
    }
};

// 具体工厂:Word 文档工厂
class WordDocumentFactory : public DocumentFactory {
public:
    Document* createDocument() const override {
        return new WordDocument();
    }
};

//4、在客户端代码中,使用不同的工厂来创建和操作不同类型的文档。
int main() {
    // 创建 PDF 文档的工厂
    DocumentFactory* pdfFactory = new PDFDocumentFactory();
    Document* pdfDoc = pdfFactory->createDocument();
    if (pdfDoc) {
        pdfDoc->open();
        delete pdfDoc; // 记得释放内存
    }
    delete pdfFactory; // 释放工厂对象

    // 创建 Word 文档的工厂
    DocumentFactory* wordFactory = new WordDocumentFactory();
    Document* wordDoc = wordFactory->createDocument();
    if (wordDoc) {
        wordDoc->open();
        delete wordDoc; // 记得释放内存
    }
    delete wordFactory; // 释放工厂对象

    return 0;
}
  • 产品接口 (Document): 定义了文档的基本操作 open()
  • 具体产品 (PDFDocumentWordDocument): 实现了 Document 接口的具体文档类型。
  • 工厂接口 (DocumentFactory): 声明了一个工厂方法 createDocument,用于创建 Document 对象。
  • 具体工厂 (PDFDocumentFactoryWordDocumentFactory): 实现了工厂接口,提供具体的产品实例。
  • 客户端代码: 使用具体的工厂来创建文档对象,并调用其方法。客户端代码只依赖于工厂接口,而不关心具体的文档类型,实现了高度的解耦。

  通过工厂方法模式,你可以轻松扩展支持更多类型的文档,只需要增加新的产品和对应的工厂类,而不需要修改客户端代码。这种方式提高了系统的可扩展性和灵活性。

3. 抽象工厂模式

  抽象工厂模式提供一个接口,用于创建相关或依赖的对象,而无需指定具体类。它一般用于创建一系列相关联的产品。

特点:

  • 可以创建一组相关的产品:产品之间有一定的关系,如同一个系列的 GUI 组件。
  • 遵循开放-关闭原则:可以添加新的产品系列,而无需修改现有代码。

示例代码1:

// 产品接口
class Button {
public:
    virtual void render() = 0;
};

class WinButton : public Button {
public:
    void render() override {
        // Windows 风格按钮
    }
};

class MacButton : public Button {
public:
    void render() override {
        // Mac 风格按钮
    }
};

class Checkbox {
public:
    virtual void paint() = 0;
};

class WinCheckbox : public Checkbox {
public:
    void paint() override {
        // Windows 风格复选框
    }
};

class MacCheckbox : public Checkbox {
public:
    void paint() override {
        // Mac 风格复选框
    }
};

// 抽象工厂
class GUIFactory {
public:
    virtual Button* createButton() = 0;
    virtual Checkbox* createCheckbox() = 0;
};

// 具体工厂
class WinFactory : public GUIFactory {
public:
    Button* createButton() override {
        return new WinButton();
    }

    Checkbox* createCheckbox() override {
        return new WinCheckbox();
    }
};

class MacFactory : public GUIFactory {
public:
    Button* createButton() override {
        return new MacButton();
    }

    Checkbox* createCheckbox() override {
        return new MacCheckbox();
    }
};

// 使用
GUIFactory* factory = new WinFactory();
Button* button = factory->createButton();
button->render();

示例代码2:

#include <iostream>
#include <string>

//定义产品接口->定义抽象工厂接口->定义具体工厂->使用抽象工厂模式

//1、首先,我们定义两个产品接口:`Document` 和 `DocumentView`,以及它们的具体实现。
// 文档接口
class Document {
public:
    virtual void open() const = 0; // 打开文档
    virtual ~Document() = default;
};

// 视图接口
class DocumentView {
public:
    virtual void render() const = 0; // 渲染视图
    virtual ~DocumentView() = default;
};

// 具体产品:PDF 文档
class PDFDocument : public Document {
public:
    void open() const override {
        std::cout << "Opening PDF document" << std::endl;
    }
};

// 具体产品:Word 文档
class WordDocument : public Document {
public:
    void open() const override {
        std::cout << "Opening Word document" << std::endl;
    }
};

// 具体视图:PDF 视图
class PDFDocumentView : public DocumentView {
public:
    void render() const override {
        std::cout << "Rendering PDF view" << std::endl;
    }
};

// 具体视图:Word 视图
class WordDocumentView : public DocumentView {
public:
    void render() const override {
        std::cout << "Rendering Word view" << std::endl;
    }
};

//2、接着,我们定义一个抽象工厂接口 `DocumentFactory`,用于创建一系列相关的文档和视图对象。
// 抽象工厂接口
class DocumentFactory {
public:
    virtual Document* createDocument() const = 0; // 创建文档
    virtual DocumentView* createView() const = 0;  // 创建视图
    virtual ~DocumentFactory() = default;
};

//3、实现具体的工厂类,每个工厂负责创建特定类型的文档和视图对象。
// 具体工厂:PDF 工厂
class PDFFactory : public DocumentFactory {
public:
    Document* createDocument() const override {
        return new PDFDocument();
    }
    
    DocumentView* createView() const override {
        return new PDFDocumentView();
    }
};

// 具体工厂:Word 工厂
class WordFactory : public DocumentFactory {
public:
    Document* createDocument() const override {
        return new WordDocument();
    }
    
    DocumentView* createView() const override {
        return new WordDocumentView();
    }
};

//4、在客户端代码中,使用不同的工厂来创建和操作不同类型的文档和视图。
int main() {
    // 创建 PDF 工厂
    DocumentFactory* pdfFactory = new PDFFactory();
    Document* pdfDoc = pdfFactory->createDocument();
    DocumentView* pdfView = pdfFactory->createView();
    if (pdfDoc) {
        pdfDoc->open();
        delete pdfDoc; // 记得释放内存
    }
    if (pdfView) {
        pdfView->render();
        delete pdfView; // 记得释放内存
    }
    delete pdfFactory; // 释放工厂对象

    // 创建 Word 工厂
    DocumentFactory* wordFactory = new WordFactory();
    Document* wordDoc = wordFactory->createDocument();
    DocumentView* wordView = wordFactory->createView();
    if (wordDoc) {
        wordDoc->open();
        delete wordDoc; // 记得释放内存
    }
    if (wordView) {
        wordView->render();
        delete wordView; // 记得释放内存
    }
    delete wordFactory; // 释放工厂对象

    return 0;
}
  • 产品接口 (DocumentDocumentView): 定义了文档和视图的基本操作。
  • 具体产品 (PDFDocumentWordDocumentPDFDocumentViewWordDocumentView): 实现了这些接口的具体类。
  • 抽象工厂接口 (DocumentFactory): 定义了创建相关产品的方法 createDocumentcreateView
  • 具体工厂 (PDFFactoryWordFactory): 实现了抽象工厂接口,负责创建特定类型的文档和视图。
  • 客户端代码: 通过工厂接口创建不同类型的文档和视图,确保了产品之间的兼容性和一致性。

  通过抽象工厂模式,客户端代码可以在不改变现有代码的情况下,灵活地添加新的文档和视图类型,同时保持了创建过程的一致性。这种模式提高了系统的可扩展性和灵活性。

4. 使用场景

  • 简单工厂模式:适用于产品类型有限、变化较小的场合。
  • 工厂方法模式:适合于产品种类较多,经常需要扩展的情况,支持可扩展设计。
  • 抽象工厂模式:适用于需要创建一系列相关产品的场景,如跨平台 GUI 系统等。

工厂方法模式 vs. 抽象工厂模式关键差异:

  1. 单一 vs. 多重产品:

    • 工厂方法模式: 主要用于创建单个产品类型的实例,适用于需要灵活地引入新的产品类型的场景。
    • 抽象工厂模式: 用于创建多个相关的产品实例,适用于需要管理一系列产品(例如,多个产品族)的场景。
  2. 工厂层次:

    • 工厂方法模式: 每个工厂方法只能创建一个产品,客户端代码依赖于具体工厂来获得不同的产品实例。
    • 抽象工厂模式: 工厂接口提供了创建一系列相关产品的方法,客户端可以使用不同的抽象工厂来创建一系列产品,保证产品的兼容性。
  3. 扩展性:

    • 工厂方法模式: 适合扩展单个产品的不同变体。
    • 抽象工厂模式: 适合扩展多个产品族,确保各个产品之间的一致性。

  总的来说,抽象工厂模式确实可以被看作是工厂方法模式的一个扩展,它在工厂方法模式的基础上增加了处理多个产品族的能力,使得系统能够更好地处理复杂的产品创建需求。

5. 总结

  工厂模式通过将对象创建的逻辑与使用逻辑分离,提高了代码的可维护性和灵活性。根据需求的复杂性,可以选择不同类型的工厂模式来实现创造性和解耦设计。适当使用工厂模式可以极大地减少代码的耦合度和复杂度。

注:水平有限,欢迎各位在评论区指导交流!!!😁😁😁

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

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

相关文章

使用Ollama本地离线体验SimpleRAG(手把手教程)

Ollama介绍 Ollama是一个开源项目&#xff0c;专注于开发和部署大语言模型&#xff0c;特别是像LLaMA这样的模型&#xff0c;用于生成高质量的文本和进行复杂的自然语言处理任务。Ollama的目标是让大语言模型的运行和使用变得更加容易和普及&#xff0c;而无需复杂的基础设施或…

解决执行npm run dev报错node: --openssl-legacy-provider is not allowed in NODE_OPTIONS

问题&#xff1a; 最近下载了一个开源系统&#xff0c;执行npm install很顺利&#xff0c;以为大功告成&#xff0c;结果运行npm run dev时报错node: --openssl-legacy-provider is not allowed in NODE_OPTIONS 解决方法&#xff1a; 应用程序配置&#xff08;package.json&a…

在控件graphicsView中实现绘图功能(三)

这里写自定义目录标题 前言&#xff1a;效果展示&#xff1a;1.图片展示2.视频展示 基础夯实&#xff1a;一.文本框焦点&#xff1a;二.QGraphicsItems&#xff1a;1.QGraphicsRectItem2.QGraphicsLineItem3.QGraphicsEllipseItem4.QGraphicsTextItem5.QGraphicsPathItem 三.鼠…

KI-DDI:知识图谱 + 大模型 + 图注意力,医学诊断

KI-DDI&#xff1a;知识图谱 大模型 图注意力&#xff0c;医学诊断 具体到点精细分析对话处理 SapBERT医学知识处理 - 图注意力网络(GAT)信息融合 - 对话嵌入 - 知识图谱嵌入知识图谱的权重 KI-DDI 图分析性关联图 知识图谱 大模型 VS KI-DDI更强的个性化 论文&#xff1a;T…

[数据集][目标检测]街灯路灯检测数据集VOC+YOLO格式1893张1类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;1893 标注数量(xml文件个数)&#xff1a;1893 标注数量(txt文件个数)&#xff1a;1893 标注…

adaptive AUTOSAR UCM模块中SoftwareCluster与Software Package是什么样的关系,他们分别包含哪些元素?

在自适应AUTOSAR(Adaptive AUTOSAR)的更新和配置管理(UCM)模块中,SoftwareCluster和Software Package是软件更新过程中的两个关键概念,它们之间有着密切的关系: SoftwareCluster:通常指的是一组功能相关的软件组件,它们共同实现了车辆中的一个或多个特定功能。在UCM中…

钓鱼的常见几种方式

钓鱼的多种方式 office钓鱼攻击 宏与宏病毒 # 宏 宏是office自带的一种高级脚本特性&#xff0c;通过VBA代码&#xff0c;可以在office中去完成某项特定的任务&#xff0c;而不必再重复相同的动作&#xff0c;目的是让用户文档中一些任务自动化# 宏病毒 宏病毒是一种寄存在文…

Qt实现圆型控件的三种方法之设置样式表

前言 最近在研究绘制各种形状的控件&#xff0c;这里专门挑出圆形的控件进行记录&#xff0c;其它形状的也大差不差&#xff0c;会了圆形的之后其它的也类似。 正文 这里我挑出Label来进行举例。 通过设置样式表 (QSS) 这种方法简单且适用于不需要自定义绘制的场景。就是要…

uniapp实现应用内检测版本更新(Android直接下载/ios跳转app store)

背景&#xff1a;最近需要给app加一个可以检测到新版本并更新的功能&#xff0c; 之前没有考虑过这个问题&#xff0c;第一次尝试&#xff0c;特此记录一下。 我在这里使用到了uniapp上的更新插件&#xff0c;并在此插件基础上进行更改以适应我的项目。 插件链接&#xff1a;ht…

【专题】2023-2024中国游戏企业研发竞争力报告合集PDF分享(附原数据表)

原文链接&#xff1a; https://tecdat.cn/?p37447 在当今的数字时代&#xff0c;游戏产业已然成为经济与文化领域中一股不可忽视的重要力量。2023 年&#xff0c;中国自研游戏市场更是呈现出一片繁荣且复杂的景象&#xff0c;实际销售收入达到了令人瞩目的 2563.8 亿元&#x…

计算机毕业设计选题推荐-民宿可视化分析-Python爬虫-随机森林算法

✨作者主页&#xff1a;IT毕设梦工厂✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Py…

Catf1ag CTF Web(九)

前言 Catf1agCTF 是一个面向所有CTF&#xff08;Capture The Flag&#xff09;爱好者的综合训练平台&#xff0c;尤其适合新手学习和提升技能 。该平台由catf1ag团队打造&#xff0c;拥有超过200个原创题目&#xff0c;题目设计注重知识点的掌握&#xff0c;旨在帮助新手掌握C…

易趋产品升级 | EasyTrack11_V2.0功能更新合集

近日&#xff0c;易趋PPM&#xff08;EasyTrack PPM&#xff09;为了帮助企业全面提升数字化项目管理能力&#xff0c;完成了新一轮的产品升级&#xff0c;从【个人空间】、【项目组合管理】、【合同与外包管理】。除了以上三大功能模块之外&#xff0c;其他升级项暂略。 1.个人…

Ajax技术详解

Ajax简介 Ajax 即 "Asynchronous Javascript And XML"&#xff08;异步 JavaScript 和 XML&#xff09;&#xff0c;是指一种创建交互式、快速动态应用的网页开发技术&#xff0c;无需重新加载整个网页的情况下&#xff0c;能够更新页面局部数据的技术。 为什么要使…

c++习题25-判断字符串是否回文

目录 一&#xff0c;题目 二&#xff0c;思路 三&#xff0c;代码 一&#xff0c;题目 描述 输入一个字符串&#xff0c;输出该字符串是否回文。回文是指顺读和倒读都一样的字符串。 输入描述 输入为一行字符串&#xff08;字符串中没有空白字符&#xff0c;字符串长度不…

Linux文件属性和打包压缩详解

1、文件属性体系 1.1 文件系统概述 [rootyunwei /]# ls -lhi 总用量 72K3505 lrwxrwxrwx. 1 root root 7 3月 7 2019 bin -> usr/bin 262152 dr-xr-xr-x. 5 root root 4.0K 12月 19 16:00 boot 399635 drwxr-xr-x 2 root root 4.0K 11月 5 2019 data1026 drw…

【数据结构】二叉树基础知识

0. 前言 在前面几期博客&#xff0c;我们已经学习过了各种线性的数据结构&#xff0c;顺序表、链表、栈、队列&#xff0c; 本期博客我们一起来学习一种非线性的结构——树 1. 树的概念及结构 1.1 树的概念 树是一种非线性的数据结构&#xff0c;它是由n&#xff08;n>…

学习嵌入式第二十九天

ipc进程间通信方式 PC&#xff0c;即进程间通信&#xff08;Inter-Process Communication&#xff09;&#xff0c;是操作系统中不同进程之间交换数据的一种机制。以下是一些常见的IPC方式&#xff1a; 管道&#xff1a;用于父子进程或兄弟进程之间的通信。消息队列&#xff…

火绒一键修复所有dll缺失?教你快速修复dll错误问题

你的电脑是否遇到过dll文件缺失的状态&#xff1f;那么应该如何将dll文件进行修复&#xff0c;不知道大家有没有听过火绒和电脑dll修复工具一键修复所有的dll缺失&#xff1f;今天我们就来了解一下如何使用火绒和电脑dll修复工具修复电脑错误dll文件丢失的问题。 dll是什么&…