C++设计模式之工厂模式

介绍与作用

工厂模式的作用主要是为了封装对象的创建,使得调用者在使用类时不必记住太多繁杂的类名即可创建对应类的对象

为了说明工厂模式,我们首先准备几个汽车类,如下


class Car
{
public:Car(string name):_name(name){};virtual void show()=0;
protected:string _name;
};class Bmw:public Car{
public:Bmw(string name):Car(name){};void show() override{cout<<"这是一辆宝马汽车!"<<endl;}
};class Audi:public Car{
public:Audi(string name):Car(name){};void show()override{cout<<"这是一辆奥迪汽车!"<<endl;}
};

正常情况下,如果我们想创建Bmw对象和Audi对象,需要使用以下方式:

    Car* p1=new Bmw("X1");Car* p2=new Audi("A2");

以上创建对象的方式下,我们需要知道该类的类名及其对应需要传入的参数。

而工厂模式要做的就是,将对象的创建封装到一个类中,只使用同一个方法来创建出所需要的对象

 

简单工厂模式

如下是简单工厂模式的代码


enum CarType
{BMW,AUDI
};class SimpleFactory
{
public:Car* createCar(CarType ct){switch(ct){case BMW:return new Bmw("X1");case AUDI:return new Audi("A2");default:cerr<<"car type input error!"<<endl;break;}return nullptr;}
};

有了工厂方法后,此时我们再需要创建对应的汽车对象时,应该为:
 

void test()
{SimpleFactory carFactory;Car* p1=carFactory.createCar(BMW);Car* p2=carFactory.createCar(AUDI);p1->show();p2->show();delete p1;delete p2;
}

如果使用智能指针创建,代码则为:

void test()
{SimpleFactory carFactory;// Car* p1=carFactory.createCar(BMW);// Car* p2=carFactory.createCar(AUDI);unique_ptr<Car> p1(carFactory.createCar(BMW));unique_ptr<Car> p2(carFactory.createCar(AUDI));p1->show();p2->show();
}

工厂方法

我们现在已经了解了工厂模式要做的事情,即封装类对象的创建,并且也已经使用了简单工厂模式的案例进行了实验。

但是上述简单工厂模式一般我们不使用,原因是它因为它没有遵守软件设计的“开闭”原则,也就是说,当我们需要再次添加或者删除一个汽车类封装一个工厂方法时,就需要直接修改SimpleFactory类中的createCar方法,这种自己修改的方法很容易在软件设计中出现问题

我们重新修改代码,来看新的工厂方法设计:


class Factory
{
public:virtual Car* carFactory(CarType ct)=0;
};//宝马工厂
class BmwFactory:public Factory{
public:Car* carFactory(CarType ct) override{if(ct==BMW){return new Bmw("X1");}return nullptr;}
};//奥迪工厂
class AudiFactory:public Factory
{
public:Car* carFactory(CarType ct) override{if(ct==AUDI){return new Audi("A2");}return nullptr;}
};

可以看到,在这段代码里,Bmw类和Audi类的工厂方法是相互独立的,这样如果我们后续想继续添加汽车类的工厂方法时,只需继承抽象的Factory方法重新一个类即可,不需要直接在原来的代码上做修改,降低了代码出错的风险

同上,代码测试如下:


void test()
{unique_ptr<Factory> bmwFptr(new BmwFactory());unique_ptr<Factory> audiFptr(new AudiFactory());unique_ptr<Car> p1(bmwFptr->carFactory(BMW));unique_ptr<Car> p2(audiFptr->carFactory(AUDI));p1->show();p2->show();
}

抽象工厂

抽象工厂与普通工厂的区别不大,其主要用于把有关联关系的,属于同一个产品簇的产品放到同一个抽象工厂类中。

具体来说,假如现在我们要生产车灯,因此要为宝马车和奥迪车都创建一个车灯类


//车灯抽象类
class CarLight
{
public:virtual void show()=0;
};//宝马车灯
class BmwLight:public CarLight
{
public:void show()override{cout<<"这是宝马车灯!"<<endl;}
};//奥迪车灯
class AudiLight:public CarLight
{
public:void show()override{cout<<"这是奥迪车灯!"<<endl;}
};

那么按照原来的工厂方法,我们为了给车灯创建工厂,就需要再增减宝马车灯工厂类和奥迪车灯工厂类,显然这种代码编写方法是很冗余的。因为我们可以看到,车灯和车之间是有联系的,他们可称之为属于同一个系列,因此我们可以将这种属于同一系列的产品放到同一个抽象类中。具体看如下代码:

 


//工厂抽象类
class Factory
{
public:virtual Car* carFactory(CarType ct)=0;virtual CarLight* createCarLight(CarType ct)=0;
};//宝马车工厂方法
class BmwFactory:public Factory{
public://宝马车创建工厂Car* carFactory(CarType ct) override{if(ct==BMW){return new Bmw("X1");}return nullptr;}//宝马车灯创建工厂CarLight* createCarLight(CarType ct)override{if(ct==BMW){return new BmwLight();}return nullptr;}
};//奥迪车工厂方法
class AudiFactory:public Factory
{
public://奥迪车工厂Car* carFactory(CarType ct) override{if(ct==AUDI){return new Audi("A2");}return nullptr;}//奥迪车灯工厂CarLight* createCarLight(CarType ct)override{if(ct==AUDI){return new AudiLight();}return nullptr;}
};

对比普通工厂模式,只是在原来的工厂类中增加了对应的虚函数方法而已。这种方式简化了代码的构建,但缺点也很明显,那就是由于抽象类的存在,因此子类必须要重写虚函数,也就意味着,即使奥迪车不需要车灯工厂,也必须要做在子类中有一个实现。

上述代码测试如下:


void test()
{unique_ptr<Factory> bmwFptr(new BmwFactory());unique_ptr<Factory> audiFptr(new AudiFactory());unique_ptr<Car> p1(bmwFptr->carFactory(BMW));unique_ptr<Car> p2(audiFptr->carFactory(AUDI));unique_ptr<CarLight> l1(bmwFptr->createCarLight(BMW));unique_ptr<CarLight> l2(audiFptr->createCarLight(AUDI));p1->show();l1->show();p2->show();l2->show();
}

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

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

相关文章

告别2023,你好,2024

夜幕降临&#xff0c;心中涌起一股暖流&#xff0c;那是家的方向。是的&#xff0c;岁月如梭&#xff0c;又到了一年的尾声。明天&#xff0c;当第一缕晨光照进车窗&#xff0c;我大概已置身于故乡的怀抱。 半载光阴&#xff0c;仿佛只是眨眼瞬间。在这段时光里&#xff0c;有些…

博弈论,LeetCode 1686. 石子游戏 VI

一、题目 1、题目描述 Alice 和 Bob 轮流玩一个游戏&#xff0c;Alice 先手。 一堆石子里总共有 n 个石子&#xff0c;轮到某个玩家时&#xff0c;他可以 移出 一个石子并得到这个石子的价值。Alice 和 Bob 对石子价值有 不一样的的评判标准 。双方都知道对方的评判标准。 给你…

CTF-show WEB入门--web21

上一阶段的信息泄露已经全部完结了&#xff0c;下一阶段的爆破也由此开始啦~~~ 下面让我们看看web21,这题是个经典的爆破问题 老样子我们先打开题目&#xff0c;查看题目提示&#xff1a; 我们可以看到题目提示为&#xff1a; 爆破什么的&#xff0c;都是基操 还有这题题目…

docker 入门教程之概述

入门指南概述 本指南包含有关如何开始使用 Docker 的分步说明。本指南向您展示如何&#xff1a; 将映像构建并作为容器运行。使用 Docker Hub 共享图像。使用带有数据库的多个容器来部署 Docker 应用程序。使用 Docker Compose 运行应用程序。 什么是容器&#xff1f; 容器…

【RPA】2分钟带你搞懂,这么火的RPA到底是什么?

2分钟带你搞懂&#xff0c;这么火的RPA到底是什么&#xff1f; 在当今数字化时代&#xff0c;机器人流程自动化&#xff08;RPA&#xff09;成为了企业数字化转型的重要组成部分。RPA是一种基于规则的软件技术&#xff0c;可以自动执行重复性、高度规范化的业务流程任务。 与传…

jsp教材管理系统Myeclipse开发mysql数据库web结构java编程计算机网页项目

一、源码特点 JSP 教材管理系统是一套完善的java web信息管理系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为TOMCAT7.0,Myeclipse8.5开发&#xff0c;数据库为Mysql5.0&…

Android应用程序的编译和打包

Android系统的APK应用程序可以有以下几种编译方式 借助系统编译&#xff1a;利用Android.mk 文件将众多小项目组织起来 借助IDE编译&#xff1a;AndroidStudio 命令行编译 &#xff1a; 比如利用gradle脚本编译APK应用。 一、 通过命令行编译和打包APK 编译命令(Window系…

bat脚本 ftp上传文件夹并递归上传子文件夹中的文件

要使用批处理脚本递归地上传整个文件夹及其子文件夹中的文件到FTP服务器&#xff0c;您可以编写一个循环结构来遍历文件夹中的所有文件&#xff0c;并使用FTP命令逐个上传它们。以下是一个示例脚本&#xff1a; echo off set FTP_SERVERftp.example.com set FTP_USERyour_usern…

没有联合和枚举 , C语言怎么能在江湖混 ?

本篇会加入个人的所谓‘鱼式疯言’ ❤️❤️❤️鱼式疯言:❤️❤️❤️此疯言非彼疯言 而是理解过并总结出来通俗易懂的大白话, 我会尽可能的在每个概念后插入鱼式疯言,帮助大家理解的. &#x1f92d;&#x1f92d;&#x1f92d;可能说的不是那么严谨.但小编初心是能让更多人能…

第二百九十六回

文章目录 1. 概念介绍2. 基本用法3. 补充用法4. 内容总结 我们在上一章回中介绍了"再谈ListView中的分隔线"&#xff0c;本章回中将介绍如何如何处理ListView中的事件冲突.闲话休提&#xff0c;让我们一起Talk Flutter吧。 1. 概念介绍 我们在第一百六十三回中介绍了…

探索C语言结构体:编程中的利器与艺术

✨✨ 欢迎大家来到贝蒂大讲堂✨✨ &#x1f388;&#x1f388;养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; 所属专栏&#xff1a;C语言学习 贝蒂的主页&#xff1a;Betty‘s blog 1. 常量与变量 1. 什么是结构体 在C语言中本身就自带了一些数据类型&#x…

LLMs之miqu-1-70b:miqu-1-70b的简介、安装和使用方法、案例应用之详细攻略

LLMs之miqu-1-70b&#xff1a;miqu-1-70b的简介、安装和使用方法、案例应用之详细攻略 目录 miqu-1-70b的简介 miqu-1-70b的安装和使用方法 1、安装 2、使用方法 miqu-1-70b的案例应用 miqu-1-70b的简介 2024年1月28日&#xff0c;发布了miqu 70b&#xff0c;潜在系列中的…

Linux系统调试课:ftrace跟踪器介绍

文章目录 一、什么是frace跟踪器?二、Ftrace 配置三、Ftrace 文件系统四、Ftrace 初体验五、函数跟踪六、Ftrace function_graph七、函数 Profiler沉淀、分享、成长,让自己和他人都能有所收获!😄 一、什么是frace跟踪器? 操作系统内核对应用开发工程师来说就像一个黑盒,…

Go语言从基础到高级-目录

一、编程入门和Go语言简介 什么是编程和为什么要学习编程计算机编程的基本概念Go语言简介为什么选择Go语言 二、设置Go语言开发环境 如何安装Go语言设置环境变量Go语言的工作空间配置 三、Go语言基础 Hello, World!初体验变量和常量数据类型&#xff08;整数、浮点数、字符…

elementUI 表格中如何合并动态数据的单元格

elementUI 表格中如何合并动态数据的单元格 ui中提供的案例是固定写法无法满足 实际开发需求 下面进行改造如下 准备数据如下 //在表格中 设置单元格的方法 :span-method"spanMethodFun" <el-table :data"tableData" border :span-method"spa…

手撕spring bean的加载过程

这里我们采用手撕源码的方式&#xff0c;开始探索spring boot源码中最有意思的部分-bean的生命周期&#xff0c;也可以通过其中的原理理解很多面试以及工作中偶发遇到的问题。 springboot基于约定大于配置的思想对spring进行优化&#xff0c;使得这个框架变得更加轻量化&#…

Backtrader 文档学习- Observers

Backtrader 文档学习- Observers 1.概述 在backtrader中运行的策略主要处理数据源和指标。 数据源被加载到Cerebro实例中&#xff0c;并最终成为策略的一部分&#xff08;解析和提供实例的属性&#xff09;&#xff0c;而指标则由策略本身声明和管理。 到目前为止&#xff0c…

LabVIEW多功能接口卡驱动

LabVIEW多功能接口卡驱动 随着自动化测试系统的复杂性增加&#xff0c;对数据采集与处理的需求不断提高。研究基于LabVIEW开发平台&#xff0c;实现对一种通用多功能接口卡的驱动&#xff0c;以支持多通道数据采集及处理功能&#xff0c;展现LabVIEW在自动化和测量领域的强大能…

FlinkCDC全量及增量采集SqlServer数据

​ 本文将详细介绍Flink-CDC如何全量及增量采集Sqlserver数据源&#xff0c;准备适配Sqlserver数据源的小伙伴们可以参考本文&#xff0c;希望本文能给你带来一定的帮助。 一、Sqlserver的安装及开启事务日志 如果没有Sqlserver环境&#xff0c;但你又想学习这块的内容&#x…

如何部署Docker Registry并实现无公网ip远程连接本地镜像仓库

文章目录 1. 部署Docker Registry2. 本地测试推送镜像3. Linux 安装cpolar4. 配置Docker Registry公网访问地址5. 公网远程推送Docker Registry6. 固定Docker Registry公网地址 Docker Registry 本地镜像仓库,简单几步结合cpolar内网穿透工具实现远程pull or push (拉取和推送)…