c++设计模式三:工厂模式

        本文通过一个例子简单介绍简单工厂模式、工厂模式和抽象工厂模式。

1.简单工厂(静态)

        假如我想换个手机,换什么手机呢?可以考虑苹果或者华为手机,那我们用简单工厂模式来实现这个功能:

        我们关注的产品是手机,那生产手机的工厂有苹果,有华为,用户也不确定选哪种怎么办?这里用简单工厂模式:定义两个枚举类型,然后写一个工厂类,根据用户传入的枚举类型生产对应品牌的手机:

手机基类:

#pragma once
class MyPhone
{
public:MyPhone(double price);~MyPhone();double getPrice() { return m_price; }
private:double m_price;
};
#include "MyPhone.h"MyPhone::MyPhone(double price) :m_price(price)
{
}
MyPhone::~MyPhone()
{
}

苹果手机:

#pragma once
#include "MyPhone.h"
class Phone_Apple:public MyPhone
{
public:Phone_Apple(double price);~Phone_Apple();
};
#include "Phone_Apple.h"
#include "stdio.h"
Phone_Apple::Phone_Apple(double price):MyPhone(price)
{printf("This is iPhone!");
}
Phone_Apple::~Phone_Apple()
{
}

华为手机:

#pragma once
#include "MyPhone.h"class Phone_Huawei :public MyPhone
{
public:Phone_Huawei(double price);~Phone_Huawei();
};
#include "Phone_Huawei.h"
#include "stdio.h"
Phone_Huawei::Phone_Huawei(double price) :MyPhone(price)
{printf("This is HUAWEI!");
}
Phone_Huawei::~Phone_Huawei()
{
}

简单工厂类:

#pragma once
#include "MyPhone.h"
enum phoneType
{APPLE_Phone,HUAWEI_Phone
};
class SimpleFactory
{
public:SimpleFactory();~SimpleFactory();MyPhone* createPhone(phoneType type);
};
#include "SimpleFactory.h"
#include "Phone_Apple.h"
#include "Phone_Huawei.h"
SimpleFactory::SimpleFactory()
{}
SimpleFactory::~SimpleFactory()
{
}
MyPhone* SimpleFactory::createPhone(phoneType type)
{MyPhone* phone = nullptr;switch (type){case APPLE_Phone:phone = new Phone_Apple(6000);break;case HUAWEI_Phone:phone = new Phone_Huawei(5600);break;}return phone;
}

用户根据自定义类型修改创建:

	SimpleFactory* factory = new SimpleFactory();//用户指定要苹果MyPhone* phone = factory->createPhone(APPLE_Phone);cout << "Price: " << phone->getPrice() << endl;delete phone;delete factory;

2.工厂模式        

        这里的工厂类似于一个手机销售店,你需要什么手机就提供什么手机,没有问题,如果用户增加了需求,想要添加三星、OPPO等,怎么办?

  1. 可以分别继承Myphone类,分别实现不同产品;
  2. 在SimpleFactory类中添加对应类别;
  3. 在createPhone方法中添加对应类别(还要引入对应的新的类和头文件);
  4. 可通过配置文件等方式准备好已有的类型供用户选择调用;

        很明显每次都要对原文件修改,如何避免呢?这里进入工厂模式:将生产手机的工厂抽象出来,然后将具体的生产厂家继承自这个抽象工厂,分别创建自己的产品:

抽象工厂类:

#pragma once
#include "MyPhone.h"
class IFactory
{
public:IFactory();~IFactory();virtual MyPhone* createPhone() = 0;
};

华为工厂:

#pragma once
#include "IFactory.h"
#include "MyPhone.h"
class HuaweiFactory : public IFactory
{
public:HuaweiFactory();~HuaweiFactory();virtual MyPhone* createPhone();
};
#include "HuaweiFactory.h"
#include "Phone_Huawei.h"
HuaweiFactory::HuaweiFactory()
{
}
HuaweiFactory::~HuaweiFactory()
{
}
MyPhone* HuaweiFactory::createPhone()
{MyPhone* phone = new Phone_Huawei(5600);return phone;
}

苹果工厂:

#pragma once
#include "IFactory.h"
#include "MyPhone.h"
class IphoneFactory :	public IFactory
{
public:IphoneFactory();~IphoneFactory();virtual MyPhone* createPhone();
};
#include "IphoneFactory.h"
#include "Phone_Apple.h"
IphoneFactory::IphoneFactory()
{
}
IphoneFactory::~IphoneFactory()
{
}
MyPhone* IphoneFactory::createPhone()
{MyPhone* phone = new Phone_Apple(6000);return phone;
}
	//3.2 工厂模式IFactory* factory = new IphoneFactory();MyPhone* phone = factory->createPhone();cout << "Price: " << phone->getPrice() << endl;IFactory* factory1 = new HuaweiFactory();MyPhone* phone1 = factory1->createPhone();cout << "Price: " << phone1->getPrice() << endl;delete phone;delete phone1;delete factory;delete factory1;

 3.抽象工厂模式

        这时,如果用户需要增加产品类型,比如华为的平板、苹果的平板怎么办?这就要引入抽象工厂模式了。 

  1. 将产品类进行抽象(这里可能会涉及多种产品);
  2. 写出具体的产品类;
  3. 将工厂类进行抽象;
  4. 具体工厂生产对应的产品(一个工厂可以生产自己品牌的多种产品)

 在原来代码基础上进行修改:

产品抽象类:

#pragma once
class MyPhone
{
public:MyPhone(double price);~MyPhone();double getPrice() { return m_price; }
private:double m_price;
};class MyPad
{
public:MyPad(double price);~MyPad();double getPrice() { return m_price; }
private:double m_price;
};

苹果产品类:

#pragma once
#include "MyPhone.h"
class Phone_Apple:public MyPhone
{
public:Phone_Apple(double price);~Phone_Apple();
};
class Pad_Apple :public MyPad
{
public:Pad_Apple(double price);~Pad_Apple();
};

华为产品类:

#pragma once
#include "MyPhone.h"class Phone_Huawei :public MyPhone
{
public:Phone_Huawei(double price);~Phone_Huawei();
};
class Pad_Huawei :public MyPad
{
public:Pad_Huawei(double price);~Pad_Huawei();
};

抽象工厂类:

#pragma once
#include "MyPhone.h"
class IFactory
{
public:IFactory();~IFactory();virtual MyPhone* createPhone() = 0;virtual MyPad* createPad() = 0;
}

具体工厂类:

#pragma once
#include "IFactory.h"
#include "MyPhone.h"
#include "Phone_Apple.h"
class IphoneFactory :	public IFactory
{
public:IphoneFactory();~IphoneFactory();virtual MyPhone* createPhone(){MyPhone* phone = new Phone_Apple(6000);return phone;}virtual MyPad* createPad() {MyPad* phone = new Pad_Apple(4900);return phone;}
};
#pragma once
#include "IFactory.h"
#include "MyPhone.h"
#include "Phone_Huawei.h"
class HuaweiFactory : public IFactory
{
public:HuaweiFactory();~HuaweiFactory();virtual MyPhone* createPhone() {MyPhone* phone = new Phone_Huawei(5600);return phone;}virtual MyPad* createPad() {MyPad* phone = new Pad_Huawei(4600);return phone;}
};

调用:

	//3.3 抽象工厂模式IFactory* factory = new HuaweiFactory();MyPad* pad = factory->createPad();cout << "Price: " << pad->getPrice() << endl;MyPhone* phone = factory->createPhone();cout << "Price: " << phone->getPrice() << endl;delete pad;delete phone;delete factory;

4.总结对比

 三种方法使用选择:

        分别理解三种模式并不困难,关键就在于根据实际问题实际情况选用不同的模式,先总结如下思考方式,供参考:

  •         当产品类型固定时可选择简单工厂模式(这里可以认为是零维的,即不用考虑其他维度);
  •         当产品涉及到不同厂商时,将工厂抽象化,不同厂商实现大厂的接口(这里可以认为是一维的,除了产品自身还需考虑厂商问题);
  •         当涉及到不同厂商的不同产品时,将工厂和产品分别抽象化(这里可以认为是二维的,考虑产品的种类以及厂商问题);

 总之要尽量满足OOP七大原则:

  • 开闭原则: 一个软件的实体应当对扩展开放,对修改关闭
  • 依赖倒转原则: 要针对接口编程,不要针对实现编程
  • 迪米特法则:只与你直接的朋友通信,而避免和陌生人通信

   三种方式的对比:

简单(静态)工厂模式:

        用来生产同一等级结构中的任意产品(对于增加新的产品,需要修改已有代码;

工厂方法模式:

        用来生产同一等级结构中的固定产品(支持增加任意产品)

        优点:

  • 你可以避免创建者和具体产品之间的紧密耦合。
  • 单一职责原则。你可以将产品创建代码放在程序的单一位置,从而使得代码更容易维护。
  • 开闭原则。无需更改现有客户端代码,你就可以在程序中引入新的产品类型。

      缺点:

  • 应用工厂方法模式需要引入许多新的子类,代码可能会因此变得更复杂。最好的情况是将该模式引入创建者类的现有层次结构中。

抽象工厂模式:

         围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂。

        优点:

  • 应用工厂方法模式需要引入许多新的子类,代码可能会因此变得更复杂。最好的情况是将该模式引入创建者类的现有层次结构中。

        缺点:

  •   由于采用该模式需要向应用中引入众多接口和类,代码可能会比之前更加复杂。

参考文献:【精选】设计模式之工厂模式(简单工厂、工厂方法、抽象工厂)_简单工厂模式 抽象工厂模式-CSDN博客1. 工厂方法模式(Factory Method) (yuque.com)2. 抽象工厂模式(Abstract Factory) (yuque.com)

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

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

相关文章

Lambda表达式与“::“方法引用 判空包装类Optional

函数式接口 函数式接口&#xff08;Functional Interface&#xff09;就是有且仅有一个抽象方法&#xff0c;但是可以有多个非抽象方法的接口。函数式接口可以被隐式转换为Lambda表达式。 Lambda表达式与"::"方法引用 方法引用主要是对Lambda表达式的一种优化&…

Scrum团队中的人

人永远是第一位的。 Scrum团队里有一个Scrum master、一个Product owner和若干个Developer。人数大概在10人左右&#xff0c;甚至更少。千万别在Scrum团队再搞什么小组&#xff0c;scrum团队就是最原子的团队了。我见过有些不专业的12人的scrum团队里&#xff0c;还安插了几个…

ubuntu部署个人网盘nextCloud使用docker-compose方式

概述 当下各大网盘的容量都是有限制的&#xff0c;而且xx云不开会员网速就拉跨。 所以就想搭建一个自己的盘&#xff0c;并且可以控制用户的权限分组&#xff1b; nextCloud就很合适 我这边都是自己用偶尔给其他人使用下&#xff0c;所以直接docker部署了。 ubuntu版本&…

Stream流基础使用

目录 Stream出现时间: 作用: 什么是 Stream? 生成流 forEach map filter limit

【2023.10.30练习】C语言-循环右移字符

计算机能力挑战初赛2020.19题 题目描述&#xff1a; 现要对一个由字符a-z和A-Z组成的字符串进行解密&#xff0c;已知加密规则是&#xff1a; 字符串中所有字符分别在大写或小写的字母表中被循环左移5位(fGh-->aBc)&#xff0c; 输入&#xff1a;一个加密过的字符串&#…

3.6每日一题(线性方程求通解)

1、判断类型选择方法&#xff1a;发现以y为未知函数&#xff0c;以x为自变量&#xff0c;不符合我们学过的类型 2、此时有两种方法&#xff1a; &#xff08;1&#xff09;x 与 y 对调&#xff0c;此时 x 为未知函数&#xff0c;y 为自变量 &#xff08;2&#xff09;变量代换…

10.27~10.29数电第三次实验分析与问题

实验要求 分析 寄存器 D触发器有两个输出口&#xff0c;一个输入口&#xff0c;一个时钟信号&#xff0c;一个复位信号 同步异步就是说复位信号在不在always里 给它加一个load就成了一位寄存器&#xff0c; 寄存器堆 8个8位的寄存器堆&#xff0c;每个寄存器都有两读一写…

GBase8a SSL 配置

GBase8a SSL 配置 GBase8a MPP Cluster 支持 SSL 标准协议&#xff0c; SSL 协议是一种安全性更高的协议标准&#xff0c; 它加入了数字签名和数字证书来实现客户端和服务器的双向身份验证&#xff0c;保证了通信双方更加安全的数据传输。 配置客户端使用 SSL 安全连接的方式连…

计算机网络 期末复习方向

一&#xff0c;网络层 前三章我们解决了在具体物理网络&#xff0c;具体局域网上数据的传输过程&#xff0c;我们能实现在一个具体网络上把数据从A主机传到B主机&#xff0c;这件事情解决了。 引入问题&#xff1a;如果网络的局域网不考虑网络的扩展问题之后&#xff0c;我们…

【Leetcode】【简单】13. 罗马数字转整数

力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台备战技术面试&#xff1f;力扣提供海量技术面试资源&#xff0c;帮助你高效提升编程技能&#xff0c;轻松拿下世界 IT 名企 Dream Offer。https://leetcode.cn/problems/roman-to-integer/description/ …

在win10下,使用torchviz对深度学习网络模型进行可视化

目录 1. 安装 graphviz 和 torchviz 2.安装 graphviz.exe 3.实例测试 4.如果你的电脑还是无法画图&#xff0c;并且出现了下面的报错&#xff1a; 5.参考文章&#xff1a; 1. 安装 graphviz 和 torchviz 首先打开 Anaconda prompt 进入自己的 pytorch 环境(图中 pt 是我自…

servlet和SpringBoot两种方式分别获取Cookie和Session方式比较(带源码) —— 图文并茂 两种方式获取Header

目录 1、cookie和Session以及Header知识点 2、设置/获取 Cookie 3、设置/获取Session 4、设置/获取Header Cookie —— 客户端机制 Session —— 服务端机制 本篇博客主在用servlet和SpringBoot两种方式分别获取Session和Cookie&#xff0c;用来比较记忆与分析 1、cookie和…

MSQL系列(十二) Mysql实战-为什么索引要建立在被驱动表上

Mysql实战-left/right/inner join 使用详解 前面我们讲解了BTree的索引结构&#xff0c;也详细讲解下 left Join的底层驱动表 选择原理&#xff0c;那么今天我们来实战一下 left join&#xff0c;right join&#xff0c;inner join 等sql命令&#xff0c;看看到底如何用以及如…

Leetcode周赛369补题(3 / 3)

目录 1、找出数组的K-or值 - 位运算 模拟 2、数组的最小相等和 - 分情况讨论 3、使数组变美的最小增量运算数 - 动态规划dp 1、找出数组的K-or值 - 位运算 模拟 100111. 找出数组中的 K-or 值 思路&#xff1a; 根据范围&#xff0c;我们可以枚举0~30位&#xff0c;然后在…

【.net core】yisha框架bootstrapTreeTable组件实现行点击事件

YiSha.Web\YiSha.Admin.Web\wwwroot\lib\bootstrap.treetable\1.0\bootstrap-treetable.js文件中替换注册行点击选中事件代码 // 注册行点击选中事件var registerRowClickEvent function () {target.find("tbody").find("tr").unbind();target.find(&quo…

数据库软考知识

分布式数据库透明性 封锁 加上共享锁之后只能加共享锁&#xff0c;加上排他锁之后&#xff0c;啥锁都不能加。 分布式数据库特性 伪传递定理 SQL函数定义&#xff0c;有点冷 来了奥&#xff0c;更冷 存储过程 很重要&#xff0c;下午第二大题也是数据库

Java精品项目源码爱心捐赠平台网站(编号V65)

Java精品项目源码扶农助农平台建设系统(编号V64) 大家好&#xff0c;小辰今天给大家介绍一个爱心捐赠平台网站(编号V65)&#xff0c;演示视频公众号&#xff08;小辰哥的Java&#xff09;对号查询观看即可 文章目录 Java精品项目源码扶农助农平台建设系统(编号V64)难度指数&a…

【Apache Flink】实现有状态函数

文章目录 在RuntimeContext 中声明键值分区状态通过ListCheckPonitend 接口实现算子列表状态使用CheckpointedFunction接口接收检查点完成通知参考文档 在RuntimeContext 中声明键值分区状态 Flink为键值分区状态&#xff08;Keyed State&#xff09;提供了几种不同的原语&…

【洛谷 P1106】删数问题 题解(贪心+字符串)

删数问题 题目描述 键盘输入一个高精度的正整数 N N N&#xff08;不超过 250 250 250 位&#xff09;&#xff0c;去掉其中任意 k k k 个数字后剩下的数字按原左右次序将组成一个新的非负整数。编程对给定的 N N N 和 k k k&#xff0c;寻找一种方案使得剩下的数字组成…

Arduino驱动ME007-ULA防水测距模组(超声波传感器)

目录 1、传感器特性 2、控制器和传感器连线图 3、驱动程序 3.1、读取串口数据