在C++中,工厂模式的思考(《C++20设计模式》及常规设计模式对比)

news2024/11/18 17:43:03

文章目录

  • 一、前言
  • 二、讲解
    • 1、构造函数的弊端
    • 2、工厂方法(解决上述弊端)
    • 3、简单工厂
      • 3.1 **UML类图**
      • 3.2 **实现**
    • 4、工厂模式
      • 4.1 **UML类图**
      • 4.2 **实现**
    • 5、抽象工厂
      • 5.1 **UML类图**
      • 5.2 **实现**
  • 三、总结

一、前言

在看《C++20设计模式》一书中产生了疑惑,就是经典工厂模式与这一书中的讲解差别很大,在此分享我心中的疑惑。

  • 讲解的方式不一样
    • 经典工厂模式分为三类 —— 简单工厂、工厂模式、抽象工厂。
    • 《C++20设计模式》一书是这么讲解的 —— 工厂方法、工厂、抽象工厂

这里说一下,其实他们两个讲解的东西都是同一个东西,只不过讲解的侧重点不同,而且《C++20设计模式》讲的东西还少了工厂模式的讲解。如果你仔细看书中的例子就会发现,这里《C++20设计模式》中的工厂指的是经典工厂中的简单工厂。

  • 如何统一经典的工厂模式和《C++20设计模式》一书中的工厂模式。

    • 他们对于为什么要有工厂模式的目的不同,也其实就是讲解方式的不同。

      • 经典工厂模式的目的: 使得创建对象的过程与使用对象的过程分离,高代码的可维护性和可扩展性。
      • 《C++20设计模式》工厂模式: 普通构造函数没法对参数进行验证,所以使用工厂模式。

下面是笔者认为,应该正确的讲解方式,可以统一 经典工厂模式的讲解和《C++20设计模式》中的工厂模式的讲解。

相关代码可以在这里,如有帮助给个star!AidenYuanDev/design_patterns_in_modern_Cpp_20

二、讲解

1、构造函数的弊端

无法对要进行创建的对象的参数进行验证
比如说救生员对象要求是八块腹肌,你却创建了六块腹肌,也即是一个不符合要求的对象,我们正常的需求肯定是不创建这个对象,或者创建一个空对像,因为它不符合要求嘛!但是构造函数却做不到这一点!

因为,构造函数不能返回任意的数据类型,

2、工厂方法(解决上述弊端)

用静态成员方法,来代替构造函数
很自然的想法嘛,既然构造函数没有返回值,那么我们就用成员函数嘛!成员函数不是有返回值嘛,那么就可以进行参数验证啦!

使用静态成员函数,是为了在创建对象之前就可以调用这个方法(因为这个方法就是为了创建对象,肯定要在创建对象之前就能调用嘛!所以用静态!

实现

#include <iostream>
#include <memory>
using namespace std;

class Product_Base {
protected:
    int a;
    int b;
    Product_Base(int a, int b) : a(a), b(b) {}
};

class Product : public Product_Base {
    int c;

protected:
    Product(int a, int b, int c) : Product_Base(a, b), c(c) {}

public:
    static unique_ptr<Product> new_product(int a, int b, int c) {
        if (a < 0) return nullptr;
        return unique_ptr<Product>(new Product(a, b, c));
    }
    void show() { cout << a << '\t' << b << '\t' << c << endl; }
};

int main() {
    unique_ptr<Product> p1 = Product::new_product(4, 5, 6);
    p1->show();
    return 0;
}

3、简单工厂

现在就是经典工厂模式的内容了,上面的代码看似已经可以很好的解决问题了,为什么还要有简单工厂呢?

  1. 使得创建对象的过程与使用对象的过程分离,提高代码的可维护性和可扩展性。(常见的答案)
  2. 工厂可以根据需要动态创建不同类型的对象,而不需要客户端了解具体的创建逻辑。
  3. 可以在工厂中判断对象与对象之间的关系。

3.1 UML类图

请添加图片描述

3.2 实现

#include <iostream>
#include <memory>
using namespace std;

class Product_Base{
protected:
    int a;
    int b;
    Product_Base(int a, int b) : a(a), b(b){}
public:
    virtual void show() = 0;
};

class Product_A : public Product_Base {
    int c;
    Product_A(int a, int b, int c) : Product_Base(a, b), c(c) {}
public:
    void show(){
        cout << a << '\t' << b << '\t' << c << endl;
    }
    static unique_ptr<Product_A> create(int a, int b, int c) {
        return unique_ptr<Product_A>(new Product_A(a, b, c));
    }
};

class Product_B : public Product_Base {
    int c;
    Product_B(int a, int b, int c) : Product_Base(a, b), c(c) {}
    
public:
    void show(){
        cout << a << '\t' << b << '\t' << c << endl;
    }
    static unique_ptr<Product_B> create(int a, int b, int c) {
        return unique_ptr<Product_B>(new Product_B(a, b, c));
    }

};

class Product_C : public Product_Base {
    int c;
    Product_C(int a, int b, int c) : Product_Base(a, b), c(c) {}
    friend class Simple_Factor;
public:
    void show(){
        cout << a << '\t' << b << '\t' << c << endl;
    }
    static unique_ptr<Product_C> create(int a, int b, int c) {
        return unique_ptr<Product_C>(new Product_C(a, b, c));
    }

};

enum class Product_Type {
    A,
    B,
    C
};

class Simple_Factor{
public:
    static unique_ptr<Product_Base> creater_producter(Product_Type type, int a, int b, int c) {
        switch (type) {
        case Product_Type::A:
            if (a < 0) return nullptr;
            return Product_A::create(a, b, c); 
            case Product_Type::B:
            if (b < 0) return nullptr;
            return Product_B::create(a, b, c);
        case Product_Type::C:
            if (c < 0) return nullptr;
            return Product_C::create(a, b, c);
        }
        return nullptr;
    }
};

int main() { 
    unique_ptr<Product_Base> p = Simple_Factor::creater_producter(Product_Type::A, 4, 5, 6);
    p->show();
    return 0; 
}

4、工厂模式

上面的简单工厂模式的缺点是当新增产品的时候就要去修改工厂的类,这就违反了开放封闭原则(对扩展开发,对修改关闭)于是,就出现了工厂方法模式,也就是工厂模式。

工厂抽象,有新的产品直接创建新的工厂就可以

4.1 UML类图

请添加图片描述

4.2 实现

#include <iostream>
#include <memory>
using namespace std;

class Product_Base{
protected:
    int a;
    int b;
    Product_Base(int a, int b) : a(a), b(b){}
public:
    virtual void show() = 0;
};

class Product_A : public Product_Base {
    int c;
    Product_A(int a, int b, int c) : Product_Base(a, b), c(c) {}
public:
    void show(){
        cout << a << '\t' << b << '\t' << c << endl;
    }
    static unique_ptr<Product_A> create(int a, int b, int c) {
        return unique_ptr<Product_A>(new Product_A(a, b, c));
    }
};

class Product_B : public Product_Base {
    int c;
    Product_B(int a, int b, int c) : Product_Base(a, b), c(c) {}
    
public:
    void show(){
        cout << a << '\t' << b << '\t' << c << endl;
    }
    static unique_ptr<Product_B> create(int a, int b, int c) {
        return unique_ptr<Product_B>(new Product_B(a, b, c));
    }

};

class Product_C : public Product_Base {
    int c;
    Product_C(int a, int b, int c) : Product_Base(a, b), c(c) {}
    friend class Simple_Factor;
public:
    void show(){
        cout << a << '\t' << b << '\t' << c << endl;
    }
    static unique_ptr<Product_C> create(int a, int b, int c) {
        return unique_ptr<Product_C>(new Product_C(a, b, c));
    }

};


enum class Product_Type {
    A,
    B,
    C
};

class Factor_Base{
public:
    virtual unique_ptr<Product_Base> new_product(int a, int b, int c) = 0;
};

class Factor_A : public Factor_Base {
public:
    unique_ptr<Product_Base> new_product(int a, int b, int c) {
        if (a < 0) return nullptr;
        return Product_A::create(a, b, c);
    }
};

class Factor_B : public Factor_Base {
public:
    unique_ptr<Product_Base> new_product(int a, int b, int c) {
        if (b < 0) return nullptr;
        return Product_B::create(a, b, c);
    }
};

int main() { 
    unique_ptr<Factor_Base> factor_a = make_unique<Factor_A>();
    unique_ptr<Product_Base> product_a = factor_a->new_product(1, 5, 7);
    product_a->show();
    return 0; 
}

5、抽象工厂

这个模式比普通的工厂模式抽象一点。可以做到一个工厂生产多个产品,比如苹果工厂可以生产苹果也就是生产苹果汁。

N个产品M个工厂。总结:工厂可以灵活的生产商品。

5.1 UML类图

请添加图片描述

5.2 实现

#include <iostream>
#include <memory>
using namespace std;

class Product_Base {
protected:
    int a;
    int b;
    Product_Base(int a, int b) : a(a), b(b) {}

public:
    virtual void show() = 0;
};

class Product_A : public Product_Base {
    int c;
    Product_A(int a, int b, int c) : Product_Base(a, b), c(c) {}

public:
    void show() { cout << a << '\t' << b << '\t' << c << endl; }
    static unique_ptr<Product_A> create(int a, int b, int c) { return unique_ptr<Product_A>(new Product_A(a, b, c)); }
};

class Product_B : public Product_Base {
    int c;
    Product_B(int a, int b, int c) : Product_Base(a, b), c(c) {}

public:
    void show() { cout << a << '\t' << b << '\t' << c << endl; }
    static unique_ptr<Product_B> create(int a, int b, int c) { return unique_ptr<Product_B>(new Product_B(a, b, c)); }
};

class Product_C : public Product_Base {
    int c;
    Product_C(int a, int b, int c) : Product_Base(a, b), c(c) {}
    friend class Simple_Factor;

public:
    void show() { cout << a << '\t' << b << '\t' << c << endl; }
    static unique_ptr<Product_C> create(int a, int b, int c) { return unique_ptr<Product_C>(new Product_C(a, b, c)); }
};

enum class Product_Type { A, B, C };

class Factor_Base {
public:
    virtual unique_ptr<Product_Base> new_product1(int a, int b, int c) = 0;
    virtual unique_ptr<Product_Base> new_product2(int a, int b, int c) = 0;
};

class Factor_A : public Factor_Base {
public:
    unique_ptr<Product_Base> new_product1(int a, int b, int c) {
        if (a < 0) return nullptr;
        return Product_A::create(a, b, c);
    }
    unique_ptr<Product_Base> new_product2(int a, int b, int c) {
        if (a < 0) return nullptr;
        return Product_B::create(a, b, c);
    }
};

class Factor_B : public Factor_Base {
public:
    unique_ptr<Product_Base> new_product1(int a, int b, int c) {
        if (b < 0) return nullptr;
        return Product_B::create(a, b, c);
    }
};

int main() {
    unique_ptr<Factor_Base> factor_a = make_unique<Factor_A>();
    unique_ptr<Product_Base> product_2 = factor_a->new_product2(1, 5, 7);
    product_2->show();
    return 0;
}

三、总结

工厂模式的优点:

  1. 使得创建对象的过程与使用对象的过程分离,高代码的可维护性和可扩展性。
  2. 使用工厂模式可以对参数进行验证。

最后如果有帮助,麻烦点个赞吧!!!

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

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

相关文章

Element 的 el-table 表格实现单元格合并

html 部分 <template><div class"index-wapper"><el-table :data"tableData" :span-method"objectSpanMethod" border><el-table-column v-for"(item, index) in tableHeader" :key"index" :prop&quo…

Qt Creator配置以及使用Git

Qt Creator配置以及使用Git 引言一、Qt Creator配置git二、Qt Creator使用git2.1 创建git仓库 or git项目导入Qt2.2 配置远端&#xff0c;拉代码 or 上传代码2.3 查看更改2.4 更多细节可参考官方文档 三、参考的博客以及文档 引言 Qt Creator配置Git之后&#xff0c;可以看作是…

严蔚敏数据结构(C语言版)吴伟民宁编著清华大学计算机系列教材+题集+配套题库+笔记+习题PDF电子版

今天分享的是 严蔚敏数据结构&#xff08;C语言版&#xff09;题集配套题库笔记习题PDF电子版 注&#xff1a;本资源搜集于网络&#xff0c;仅供学习交流&#xff0c;严禁用于商业用途 内容简介&#xff1a; “数据结构”是计算机程序设计的重要理论技术基础&#xff0c;它…

pdf怎么拆分成一页一页?4种拆分方法分享

在日常的办公学习中&#xff0c;PDF文档因其跨平台、易阅读、不易篡改等特性&#xff0c;成为我们工作和学习中不可或缺的一部分。然而&#xff0c;当我们需要对PDF进行编辑、打印或分享时&#xff0c;有时需要将整个PDF文档拆分成一页一页的单独文件。那么&#xff0c;如何高效…

Hive测试

1、数据仓库的体系结构包含四个层次&#xff0c;分别是&#xff1a; 数据源 数据存储和管理 数据服务 数据应用 2、Hive提供了类似关系数据库SQL的查询语言&#xff1a; HiveQL 3、Hive某种程度上可以看作 用户编程接口&#xff0c;本身不存储和处理数据&#xff0c;存储数据依…

【RAG】Dense X Retrivel:合适的检索粒度对RAG的重要性(浅看命题粒度的定义及分解方法)

前言 在传统的密集检索中&#xff0c;常见的检索单元包括文档、段落或句子。然而&#xff0c;这些单元的选择可能会影响检索性能和下游任务的效果。例如&#xff0c;段落可能包含与问题无关的额外细节&#xff0c;而句子可能过于复杂或缺乏必要的上下文信息。为了解决这些问题…

Spring中使用FactoryBean配置注入Bean

spring通过FactoryBean配置可以将第三方框架整合到Spring中来&#xff0c;FactoryBean配置如下&#xff1a; 写一个用于注于的实体类&#xff0c;如User,并对这个类写一个实现FactoryBean的中间类&#xff08;UserFactoryBean&#xff09; User类 /** *Description: *author…

MySQL的下载安装

1.软件的下载 打开官网下载mysql-installer-community-8.0.37.0.msi 2.软件的安装 mysql下载完成后&#xff0c;找到下载文件&#xff0c;双击安装 3.配置环境变量 4.自带客户端登录与退出

【前端VUE】VUE3第一节—vite创建vue3工程

什么是VUE Vue (发音为 /vjuː/&#xff0c;类似 view) 是一款用于构建用户界面的 JavaScript 框架。它基于标准 HTML、CSS 和 JavaScript 构建&#xff0c;并提供了一套声明式的、组件化的编程模型&#xff0c;帮助你高效地开发用户界面。无论是简单还是复杂的界面&#xff0…

使用Python绘制彩虹效果:动态彩虹动画

文章目录 引言准备工作前置条件 代码实现与解析导入必要的库初始化Pygame定义绘制彩虹函数定义颜色列表主循环 完整代码 引言 彩虹是自然界中最美丽的现象之一。通过编程&#xff0c;我们可以将这一奇妙的景象带到屏幕上。在这篇博客中&#xff0c;我们将使用Python来创建一个…

Q-Vision新功能发布 | CANReplay-enable发送

Q-Vision是一款网络分析与ECU测试工具软件&#xff0c;支持CAN&#xff08;FD&#xff09;、LIN、以太网、LVDS等车载网络标准&#xff0c;以及CCP/XCP/UDS/OBD等协议&#xff0c;并能导入DBC/LDF/ARXML/A2L/ODX等格式的数据库。 使用Q-Vision可实现对多种总线网络的在线记录、…

k8s学习--k8s群集ELK日志收集部署最详细的过程与应用(收集k8s群集日志)(图形化界面手把手教学)

文章目录 FilebeatFilebeat主要特点Filebeat使用场景 ELK简介Elasticsearch简介Elasticsearch主要特点Elasticsearch使用场景 Logstash简介Logstash主要特点Logstash使用场景 Kibana简介Kibana主要特点Kibana使用场景 简单理解 环境一、ELK集群部署1.软件安装2.软件配置及启动(…

golang写的自动更新器

文件自动更新器&#xff0c;这个很多端游和软件都有用到的。 golang的rpc通信&#xff0c;是非常好用的一个东西&#xff0c;可以跟调用本地函数一样&#xff0c;调用远程服务端的函数&#xff0c;直接从远程服务端上拉取数据下来&#xff0c;简单便捷。 唯一的遗憾就是&#x…

SQLite 命令行客户端 + Windows 批处理应用

SQLite 命令行客户端 Windows 批处理应用 下载 SQLite 客户端1. Bat 辅助脚本1. 执行SQL.bat执行 2. 导出Excel.bat执行效果 3. 导出HTML.bat执行效果 4. 清空-订单表.bat 2. 测试 SQL1. 创建订单表.sql2. 插入订单表.sql3. 查询订单表.sql4. 清空订单表.sql5. 删除订单表.sql…

html + css 快速实现订单详情的布局demo

突然安排让速写这样的一个布局&#xff0c;重点就是CSS画一条虚线,并且还要灵活设置虚线的宽度和虚线之间的间隔和虚线的颜色。 注&#xff1a;订单里面的金额都是随意写的 <!DOCTYPE html> <html lang"zh-CN"><head><meta charset"UTF-8&…

从零到一:全面掌握模板机部署与虚拟机克隆的实训指南【实训Day01】

一、模板机的部署 1.设置好子网ip和子网掩码以及网关 保证有NAT模式 子网IP着重记住前三段&#xff1a;192.168.222.xxx 2.开始建立虚拟机 注意&#xff1a;路径中不要有中文 3.安排8个内核 4.安排2GB内存 创建初步成功&#xff0c;接着 5.开启此虚拟机 6.选择中文 7.日期&…

Go语言特点、编译及命令

本文主要分为三部分内容分别为&#xff1a;Go语言的特点介绍&#xff1b;编译windows、linux环境文件及Go命令。 目录 Go语言特点 编译文件 编译window文件 编译linux文件 Go命令&#xff08;build/run/install/env&#xff09; 编译文件 直接运行程序 安装程序 配置G…

平衡二叉查找树和多路查找树

平衡二叉查找树 普通平衡二叉查找树 平衡二叉树定义是按照有序排列成树状&#xff0c;左子树数据大于右子树&#xff0c;任意节点的左右子树高度不能大于1 优点&#xff1a;可以保证绝对的平衡 缺点&#xff1a;当进行删除节点和新增节点&#xff0c;树进行自平衡的时候&…

10 docker 安装 mysql详解

目录 一、安装mysql 1. 镜像获取 2. 简单版 2.1. 使用mysql镜像 2.2. 建库建表插入数据 2.3. 外部win 连接并插入中文数据测试 2.4. 简单版本问题 3. 创建容器-实战版 3.1. 新建容器实列并挂载卷 3.2. 新建my.cnf , 解决中文乱码 3.3. 重启容器验证 3.4. 总结 一、…

VScode使用ssh连接服务器

VScode是一款有丰富插件的编译器&#xff0c;非常好用&#xff01;除非你不会用&#xff0c;因为太过繁琐或着频繁出错导致想把电脑砸了&#xff1b; 插件选择 ssh 配置文件 Host myblablaHostName xxx.xx.xxx.xxxUser username用户名一般是服务器上创建有什么用户名&#xf…