C++设计模式(更新中)

devtools/2024/9/24 12:54:10/

文章目录

  • 1、创建型模式
    • 1.1 简单工厂(Simple Factory)
      • (1)示例
      • (2)总结
    • 1.2 工厂方法(Factory Method)
      • (1)示例
      • (2)总结
    • 1.3 抽象工厂(Abstract Factory)
      • (1)示例
      • (2)总结
    • 1.4 建造者(Builder)
      • (1)示例
      • (2)总结

1、创建型模式

1.1 简单工厂(Simple Factory)

简单工厂模式通过一个工厂类负责对象的创建,客户端只需提供参数,工厂类根据条件返回相应的对象。这种方式可以减少客户端对具体实现的依赖。

核心结构

  1. 工厂类:负责根据传入的参数生成具体对象。
  2. 产品抽象基类:定义所有产品的公共接口。
  3. 产品类:具体的实现类,继承自产品基类。
  4. 客户端:通过工厂类创建产品对象并调用其方法。
    在这里插入图片描述

(1)示例

假设正在开发一个工具库,工具种类包含绘制工具 (DrawTool) 和擦除工具 (EraseTool)。为了简化工具对象的创建和使用,可以通过简单工厂模式来生成这些工具。

库代码实现

  • 工具抽象类和具体工具类
    Tool.h:为了方便,这里没有分文件编写,实际中也可以分成Tool.h + Tool.cpp,工具太多也可以每个具体工具和工具接口类全部分开编写。之后的示例代码也是一样。

    #pragma once
    #include <iostream>// 工具基类
    class Tool
    {
    public:virtual void UseTool() = 0; virtual ~Tool() = default;
    };// 具体工具类1 - 绘制工具
    class DrawTool : public Tool
    {
    public:void UseTool() override{std::cout << "Using Draw Tool!" << std::endl;}
    };// 具体工具类2 - 擦除工具
    class EraseTool : public Tool
    {
    public:void UseTool() override{std::cout << "Using Erase Tool!" << std::endl;}
    };
    
  • 工厂类 (ToolFactory.hToolFactory.cpp)
    ToolFactory.h

    #pragma once
    #include <memory>
    #include "Tool.h"// 工厂类,用于创建工具对象
    class ToolFactory
    {
    public:enum class ToolType{Draw,Erase};// 简单工厂创建工具的静态方法static std::unique_ptr<Tool> CreateTool(ToolType type);
    };
    

    ToolFactory.cpp

    #include "ToolFactory.h"std::unique_ptr<Tool> ToolFactory::CreateTool(ToolType type)
    {switch (type){case ToolType::Draw:return std::make_unique<DrawTool>();case ToolType::Erase:return std::make_unique<EraseTool>();default:return nullptr;}
    }
    

使用者代码

使用者只需要引入 ToolFactory.h,通过工厂类创建工具对象,而不需要知道具体的工具类 DrawToolEraseTool。这减少了客户端和库中具体实现的依赖关系。

从下面代码,可以看到,实际上使用者这边还是需要依赖于工具基类的信息的(没有包含Tool.h是因为工厂类已经包含了),但是不需要知道具体的工具类的信息

Main.cpp

#include "ToolFactory.h" int main()
{// 使用工厂创建绘制工具std::unique_ptr<Tool> tool = ToolFactory::CreateTool(ToolFactory::ToolType::Draw);if (tool)tool->UseTool(); // 使用工厂创建擦除工具tool = ToolFactory::CreateTool(ToolFactory::ToolType::Erase);if (tool)tool->UseTool();return 0;
}

(2)总结

  • 优点:

    • 减少依赖:客户端只需依赖工厂类与产品基类,而不需要依赖具体产品类。
    • 解耦创建与使用:工厂类封装了创建细节,客户端只负责使用产品。
  • 缺点:

    • 违反开闭原则:每次添加新的工具类型,必须修改工厂类的 CreateTool 方法,添加一条新的case:,导致不符合 “对修改关闭,对扩展开放”的原则。在扩展性和代码维护上有一定的缺点,特别是在需要频繁添加新工具类型的情况下。
    • 工厂类复杂化:工厂类会随产品种类的增加而变得复杂。

1.2 工厂方法(Factory Method)

工厂方法模式通过将创建对象的逻辑推迟到子类中,从而解决简单工厂模式违反开闭原则的问题。它的核心理念是将工厂类拆分成多个具体工厂,每个工厂类负责创建某种特定产品。

核心结构

抽象产品 + 具体产品,抽象工厂 + 具体工厂
在这里插入图片描述

(1)示例

重构1.1中的示例代码,工具基类及其子类保持不变,但工厂类需要改造成每种产品对应一个工厂子类,各自负责产品的创建。

  • 工厂类ToolFactory.h
    #pragma once
    #include <memory>
    #include "Tool.h"class ToolFactory 
    {
    public:virtual std::unique_ptr<Tool> CreateTool() = 0;virtual ~ToolFactory() = default;
    };class DrawToolFactory : public ToolFactory 
    {
    public:std::unique_ptr<Tool> CreateTool() override {return std::make_unique<DrawTool>();}
    };class EraseToolFactory : public ToolFactory 
    {
    public:std::unique_ptr<Tool> CreateTool() override {return std::make_unique<EraseTool>();}
    };
    
  • 客户端使用main.cpp
    #include "ToolFactory.h"int main()
    {// 创建绘制工具工厂对象std::unique_ptr<ToolFactory> drawFactory = std::make_unique<DrawToolFactory>();// 创建绘制工具std::unique_ptr<Tool> drawTool = drawFactory->CreateTool();if (drawTool)drawTool->UseTool();// 创建擦除工具工厂对象std::unique_ptr<ToolFactory> eraseFactory = std::make_unique<EraseToolFactory>();// 创建擦除工具std::unique_ptr<Tool> eraseTool = eraseFactory->CreateTool();if (eraseTool)eraseTool->UseTool();return 0;
    }
    

(2)总结

  • 优点

    • 符合开闭原则:新增产品时,只需新增对应的工厂类,而无需修改已有代码。
    • 单一职责:每个工厂类只负责创建一种产品,职责更为明确。
    • 可扩展性:更方便地增加新产品和新工厂,且代码修改局部化,减少潜在错误。
  • 缺点

    • 类的增加:每种产品都需要对应的工厂类,会导致类的数量增加,增加系统的复杂性。
    • 复杂度提升:对于简单的对象创建场景,工厂方法模式可能显得过于复杂,因为每个具体产品都需要单独的工厂类。
    • 职责分散:每个工厂类只负责创建某种具体产品,可能导致系统中存在较多分散的创建逻辑,维护起来可能不便。

1.3 抽象工厂(Abstract Factory)

在某些情况下,我们需要创建一组相关或依赖的对象,而不仅仅是单个对象。工厂方法模式虽然可以创建单个产品,但如果需要创建多个产品,并且这些产品之间存在某种强关联(比如 UI 系统中不同平台的按钮、文本框等,比如windows平台的按钮必须搭配同平台的文本框),工厂方法模式就显得不足了。

抽象工厂模式提供一个接口,用于创建相关联的对象族,而不指定它们的具体类。它解决了创建多类相关产品的问题,而不是单个产品。

核心结构:

  1. 抽象工厂类:定义创建一系列相关产品的接口。
  2. 具体工厂类:实现抽象工厂的接口,负责创建一系列具体产品。
  3. 抽象产品类:为不同产品提供统一的接口。
  4. 具体产品类:每个具体工厂创建的不同产品实现类。
  5. 客户端:通过抽象工厂接口来获取相关联的产品。
    在这里插入图片描述

(1)示例

假设现在有4种具体工具产品,分别是2D绘制工具、2D擦除工具、3D绘制工具、3D擦除工具,正常情况来说,2D绘制工具跟2D擦除工具肯定是配套的,如果工厂方法来创建,就可能混淆使用,而抽象工厂则可以避免这种错误产生

  • Tool.h:这里从Tool接口类直接派生了4个工具,也可以这样做(根据需求灵活处理):

    • 定义2DTool抽象类和3DTool抽象类,然后再分别派生2个具体工具类出来
    • 定义DrawTool抽象类和EraseTool抽象类,然后再分别派生2个具体工具类出来
    #pragma once
    #include <iostream>// 抽象产品类 - 工具
    class Tool 
    {
    public:virtual void UseTool() = 0;virtual ~Tool() = default;
    };// 具体产品类 - 2D绘制工具
    class Draw2DTool : public Tool 
    {
    public:void UseTool() override {std::cout << "Using 2D Draw Tool!" << std::endl;}
    };// 具体产品类 - 2D擦除工具
    class Erase2DTool : public Tool 
    {
    public:void UseTool() override {std::cout << "Using 2D Erase Tool!" << std::endl;}
    };// 具体产品类 - 3D绘制工具
    class Draw3DTool : public Tool 
    {
    public:void UseTool() override {std::cout << "Using 3D Draw Tool!" << std::endl;}
    };// 具体产品类 - 3D擦除工具
    class Erase3DTool : public Tool 
    {
    public:void UseTool() override {std::cout << "Using 3D Erase Tool!" << std::endl;}
    };
    
  • Factory.h

    #pragma once
    #include <memory>
    #include "Tool.h"// 抽象工厂类
    class ToolFactory 
    {
    public:virtual std::unique_ptr<Tool> CreateDrawTool() = 0;virtual std::unique_ptr<Tool> CreateEraseTool() = 0;virtual ~ToolFactory() = default;
    };// 具体工厂类 - 2D工具工厂
    class Tool2DFactory : public ToolFactory 
    {
    public:std::unique_ptr<Tool> CreateDrawTool() override {return std::make_unique<Draw2DTool>();}std::unique_ptr<Tool> CreateEraseTool() override {return std::make_unique<Erase2DTool>();}
    };// 具体工厂类 - 3D工具工厂
    class Tool3DFactory : public ToolFactory 
    {
    public:std::unique_ptr<Tool> CreateDrawTool() override {return std::make_unique<Draw3DTool>();}std::unique_ptr<Tool> CreateEraseTool() override {return std::make_unique<Erase3DTool>();}
    };
    
  • main.cpp

    // Main.cpp
    #include "Factory.h"int main() {// 创建2D工厂std::unique_ptr<ToolFactory> factory2D = std::make_unique<Tool2DFactory>();std::unique_ptr<Tool> tool = factory2D->CreateDrawTool();tool->UseTool(); tool = factory2D->CreateEraseTool();tool->UseTool(); // 创建3D工厂std::unique_ptr<ToolFactory> factory3D = std::make_unique<Tool3DFactory>();tool = factory3D->CreateDrawTool();tool->UseTool();  tool = factory3D->CreateEraseTool();tool->UseTool(); return 0;
    }
    

(2)总结

  • 优点

    • 产品族一致性:通过具体工厂类,确保了 2D 和 3D 工具的内部一致性,避免在客户端中创建不兼容的工具(如混用 2D 和 3D 工具)。
    • 在新增工具方面,遵守开闭原则:新增其他绘制工具类型时,比如一个4D工具,只需额外实现新的具体工厂类和对应的产品类,保证了系统的可扩展性。
    • 减少客户端依赖:客户端只依赖抽象工厂和抽象产品,而不需要关心具体实现的细节,符合依赖倒置原则。
  • 缺点

    • 复杂性:增加了工厂类和产品类的数量,系统结构更加复杂,尤其当产品族多时会造成一定的维护压力。
    • 在新增每种工具的功能时,不遵守开闭原则:比如现在的每种工具套件都支持绘制、擦除工具,我还想新增一个修改工具,则需要修改每一个工厂类和每一个产品类的代码。
    • 灵活性降低:所有工厂创建的产品都是预定义的,无法灵活组合不同种类的产品。

1.4 建造者(Builder)

建造者模式的作用是将复杂对象的组装过程和对象的实际表示区分开来。这样,相同的组装步骤可以用来制造出不同的对象表现形式。这种模式非常适合于那些由多个可选组件组成的复杂对象。通过分步骤地构建每个组件,我们可以根据需要灵活地组合出各种不同的对象。

建造者模式的核心理念是通过引入一个建造者(Builder)类,负责构建对象的各个部分,而不是在一个复杂的构造函数中完成所有工作。它能够分阶段创建对象,允许客户端在构建过程中灵活选择和配置对象的组件。该模式还允许通过实现不同的具体建造者,来生成不同类型或版本的产品对象。

直接上具体案例,来理解这个模式

(1)示例

组装电脑,电脑有很多可定制的部件(如CPU、GPU、硬盘、内存等),对于不同的需求(比如游戏电脑、办公电脑),需要选用不同的性能、品牌的部件,建造者模式可以灵活地应对这些变化。

在这里插入图片描述
类之间的关系

  1. Director:通过 Builder 来控制建造流程,确保每个步骤按顺序调用。它不需要知道具体的产品细节,只需要负责调用 Builder 提供的接口。
  2. Builder:定义了构建的接口,具体的建造步骤(如 BuildCPU()BuildGPU() 等)。
  3. ConcreteBuilder:实现 Builder 接口的具体类,负责创建不同类型的产品,如游戏电脑和办公电脑。每个 ConcreteBuilder 实现了相同的建造步骤,但生成不同的结果。
  4. Product:是最终生成的对象,它包含了多个部分(CPU、GPU、Memory 等),由 ConcreteBuilder 一步步构建。
#include <iostream>
#include <string>// 产品类:电脑
class Computer 
{
public:void SetCPU(const std::string& cpu) { CPU = cpu; }void SetGPU(const std::string& gpu) { GPU = gpu; }void SetMemory(const std::string& memory) { Memory = memory; }void SetStorage(const std::string& storage) { Storage = storage; }void ShowSpecifications() const {std::cout << "Computer Specifications:\n";std::cout << "CPU: " << CPU << "\n";std::cout << "GPU: " << GPU << "\n";std::cout << "Memory: " << Memory << "\n";std::cout << "Storage: " << Storage << "\n";}private:std::string CPU;std::string GPU;std::string Memory;std::string Storage;
};// 抽象建造者:定义创建产品的步骤
class ComputerBuilder 
{
public:virtual ~ComputerBuilder() = default;virtual void BuildCPU() = 0;virtual void BuildGPU() = 0;virtual void BuildMemory() = 0;virtual void BuildStorage() = 0;virtual Computer* GetResult() = 0;
};// 具体建造者:游戏电脑
class GamingComputerBuilder : public ComputerBuilder 
{
private:Computer* computer;public:GamingComputerBuilder() {computer = new Computer();}~GamingComputerBuilder() {delete computer;}void BuildCPU() override {computer->SetCPU("i9");}void BuildGPU() override {computer->SetGPU("RTX4090 ");}void BuildMemory() override {computer->SetMemory("16GB DDR4");}void BuildStorage() override {computer->SetStorage("1TB SSD");}Computer* GetResult() override {return computer;}
};// 具体建造者:办公电脑
class OfficeComputerBuilder : public ComputerBuilder 
{
private:Computer* computer;public:OfficeComputerBuilder() {computer = new Computer();}~OfficeComputerBuilder() {delete computer;}void BuildCPU() override {computer->SetCPU("i3");}void BuildGPU() override {computer->SetGPU("GTX 960");}void BuildMemory() override {computer->SetMemory("8GB DDR4");}void BuildStorage() override {computer->SetStorage("512GB SSD");}Computer* GetResult() override {return computer;}
};// 指挥者:控制建造流程
class Director 
{
private:ComputerBuilder* builder = nullptr;
public:void SetBuilder(ComputerBuilder* builder) {this->builder = builder;}void Construct() {builder->BuildCPU();builder->BuildGPU();builder->BuildMemory();builder->BuildStorage();}
};// 客户端代码
int main() 
{Director director;// 创建游戏电脑GamingComputerBuilder gamingBuilder;director.SetBuilder(&gamingBuilder);director.Construct();Computer* gamingComputer = gamingBuilder.GetResult();gamingComputer->ShowSpecifications();// 创建办公电脑OfficeComputerBuilder officeBuilder;director.SetBuilder(&officeBuilder);director.Construct();Computer* officeComputer = officeBuilder.GetResult();officeComputer->ShowSpecifications();delete gamingComputer;delete officeComputer;return 0;
}

(2)总结

  • 优点:

    • 灵活应对不同需求:在这个例子中,游戏电脑和办公电脑虽然都是由CPU、GPU、内存和硬盘等部件组成,但具体的配置差别很大。通过 Director 控制构建顺序,我们可以复用建造流程,但根据不同的 具体ConcreteBuilder 实现来生成不同的产品。这使得我们在不修改整体构建逻辑的情况下,可以灵活地创建多种不同的对象。

    • 易扩展:建造者模式将复杂对象的构建过程与具体的构建实现解耦。Director 只需要知道如何控制创建流程,具体怎么创建、用什么配置并不重要。通过这种解耦,可以很容易地替换或扩展新的 ConcreteBuilder,比如还可以再实现一个 WorkstationComputerBuilder,用于构建工作站电脑。

    • 代码更加清晰:对于复杂对象的构建,通过一步步调用建造方法(如 BuildCPU()BuildMemory()),构建过程非常清晰易懂。这种模式避免了构建过程中混乱的条件判断和冗长的构造代码。

  • 缺点:

    • 增加代码复杂度:模式引入了额外的建造者、指导者等类,可能会使代码量增多,结构变复杂。
    • 多次构建成本高:如果每次构建都需要重建所有对象部分,可能导致性能开销。

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

相关文章

叶片检测系统源码分享

叶片检测检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer Vision …

Leetcode算法基础篇-分治算法

分治算法 「分而治之」&#xff0c;就是把一个复杂的问题分成两个或更多的相同或相似的子问题&#xff0c;直到最后子问题可以简单的直接求解&#xff0c;原问题的解即子问题的解的合并。 我们发现分治算法的思想和递归相似&#xff0c;实际上 递归算法∈分治算法 解题步骤&am…

alias 后门从入门到应急响应

目录 1. alias 后门介绍 2. alias 后门注入方式 2.1 方式一(以函数的方式执行) 2.2 方式二(执行python脚本) 3.应急响应 3.1 查看所有连接 3.2 通过PID查看异常连接的进程&#xff0c;以及该进程正在执行的命令行命令 3.3 查看别名 3.4 其他情况 3.5 那么检查这些…

在idea里运行swing程序正常,但是在外部运行jar包却报错,可能是jdk版本问题

在idea里运行swing程序异常&#xff0c;报Caused by: java.awt.HeadlessException错误 System.setProperty("java.awt.headless","false");加上这句话

通信工程学习:什么是VM虚拟机

VM&#xff1a;虚拟机 VM虚拟机&#xff08;Virtual Machine&#xff09;是一种通过软件模拟的计算机系统&#xff0c;它能够在物理计算机上模拟并运行多个独立的虚拟计算机系统。以下是关于VM虚拟机的详细解释&#xff1a; 一、VM虚拟机的定义与原理 定义&#xff1a; VM虚拟…

路由器的天线有什么用?数量多≠信号强?

你是否也曾凝视着路由器上那几根或长或短的天线&#xff0c;心中暗自嘀咕&#xff1a;“这些天线到底有啥用&#xff1f;是不是天线越多&#xff0c;信号就越强呢&#xff1f;”今天&#xff0c;让我们一同揭开这一谜团&#xff01; 一、路由器天线的核心作用 1. 信号发射与接…

基于单片机的风机故障检测装置的设计与实现(论文+源码)

1 系统总体设计方案 通过对风机故障检测装置的设计与实现的需求、可行性进行分析&#xff0c;本设计风机故障检测装置的设计与实现的系统总体架构设计如图2-1所示&#xff0c;系统风机故障检测装置采用STM32F103单片机作为控制器&#xff0c;并通过DS18B20温度传感器、ACS712电…

11年计算机考研408-数据结构

设执行了k次。 解析&#xff1a; d要第一个出&#xff0c;那么abc先入栈&#xff0c;d入栈然后再出栈&#xff0c;这前面是一个固定的流程&#xff0c;后面就很灵活了&#xff0c;可以ecba&#xff0c;ceba&#xff0c;cbea&#xff0c;cbae。 答案是4个序列。 解析&#xff1a…