03创建型设计模式——抽象工厂模式

news/2024/9/17 19:08:55/ 标签: 设计模式, 抽象工厂模式

一、抽象工厂模式简介

        抽象工厂模式是所有形态的工厂模式中最为抽象和具有一般性的。抽象工厂模式可以向客户端提供一个接口,使得客户端在不必指定产品的具体类型的情况下,能够创建多个产品族的产品对象。例如现实生活中,水果的种类繁多,众多水果中,桃子也分很多种类,还分地域,比如北方的猕猴桃,南方的猕猴桃等。而抽象工厂模式就是模拟现实中的分类,实现生产一个产品组(相对工厂模式,抽象工厂模式可以生产一个产品的族)。抽象工厂模式的核心思想是定义一个用于创建一系列相关或相互依赖对象的接口,而不需要指定具体类。这种模式的主要优点是它将对象的创建和使用分开,使得系统更具扩展性和灵活性。

产品等级和产品族的关系

工厂模式(具体)和抽象工厂模式(抽象)的对比解释:

具体工厂在开闭原则下, 能生产香蕉/苹果/梨子;  (产品等级结构)

抽象工厂:在开闭原则下, 能生产:南方香蕉/苹果/梨子 (产品族)

北方香蕉/苹果/梨子

GoF一书中对抽象工厂模式的介绍

二、抽象工厂模式的用处

1. 创建相关或相互依赖的对象

抽象工厂模式可以确保客户端只通过工厂接口创建一组相关或相互依赖的对象,而不需要了解具体的对象类。这种方法可以确保这些对象能够一起正常工作。例如,我的代码中,NorthFactory 和 SouthFactory 创建了一系列北方或南方的水果,确保了这些水果的创建和使用是一致的。

2. 提供对象的跨平台支持

当系统需要在不同平台或环境下运行时(如不同操作系统、不同硬件架构等),抽象工厂模式可以提供一个统一的接口来创建对象,从而简化了跨平台的对象创建过程。例如,一个应用程序可以根据不同的操作系统(Windows、Linux、MacOS)选择不同的工厂来创建界面组件。

3. 促进系统的扩展和维护

抽象工厂模式使得系统的扩展变得更加容易。添加新的产品或产品家族时,只需要添加新的工厂类和产品类,而不需要修改现有的客户端代码。这种模式隔离了客户端与具体产品的创建细节,减少了代码的耦合度,从而使系统更容易维护和扩展。

4. 实现产品的一致性

通过将对象的创建过程封装在工厂类中,可以确保创建出的对象具有一致的风格或特点。这在需要保证一组对象之间的兼容性或一致性时尤其重要。例如,我的代码中,不同的工厂保证了北方和南方水果的一致性。

5. 支持不同的产品变种

如果一个系统需要支持多种变种的产品(例如不同风味、不同颜色的产品),抽象工厂模式可以通过不同的工厂实现来支持这些变种。每个工厂可以生成一组产品的不同变种,这样系统可以根据需求选择合适的工厂来获取相应的产品。

三、抽象工厂模式的设计方法

 abstractFactory.cpp

#include <iostream>
#include <cstring>using namespace std;class Fruit
{
public:virtual void getProduct() = 0;virtual ~Fruit() = default;
};class AbstractFactory
{
public:virtual Fruit* CreateProduct(const std::string& type) = 0;virtual ~AbstractFactory() = default;
};class NorthBanana : public Fruit
{
public:void getProduct() override{std::cout << "北方香蕉 " << std::endl;}
};class NorthApple : public Fruit
{
public:void getProduct() override{std::cout << "北方苹果 " << std::endl;}
};class NorthOrange : public Fruit
{
public:void getProduct() override{std::cout << "北方橙子 " << std::endl;}
};class NorthWatermelon : public Fruit
{
public:void getProduct() override{std::cout << "北方西瓜 " << std::endl;}
};class NorthCherry : public Fruit
{
public:void getProduct() override{std::cout << "北方樱桃 " << std::endl;}
};class NorthPeach : public Fruit
{
public:void getProduct() override{std::cout << "北方桃子 " << std::endl;}
};class NorthPear : public Fruit
{
public:void getProduct() override{std::cout << "北方梨 " << std::endl;}
};class SouthBanana : public Fruit
{
public:void getProduct() override{std::cout << "南方香蕉" << std::endl;}
};class SouthApple : public Fruit
{
public:void getProduct() override{std::cout << "南方苹果" << std::endl;}
};class SouthOrange : public Fruit
{
public:void getProduct() override{std::cout << "南方橙子" << std::endl;}
};class SouthWatermelon : public Fruit
{
public:void getProduct() override{std::cout << "南方西瓜" << std::endl;}
};class SouthCherry : public Fruit
{
public:void getProduct() override{std::cout << "南方樱桃" << std::endl;}
};class SouthPeach : public Fruit
{
public:void getProduct() override{std::cout << "南方桃子" << std::endl;}
};class SouthPear : public Fruit
{
public:void getProduct() override{std::cout << "南方梨" << std::endl;}
};class NorthFactory : public AbstractFactory
{Fruit * CreateProduct(const std::string& type) override{if (type == "北方香蕉") return new NorthBanana;if (type == "北方苹果") return new NorthApple;if (type == "北方橙子") return new NorthOrange;if (type == "北方西瓜") return new NorthWatermelon;if (type == "北方樱桃") return new NorthCherry;if (type == "北方桃子") return new NorthPeach;if (type == "北方梨") return new NorthPear;return nullptr;}
};class SouthFactory : public AbstractFactory
{Fruit * CreateProduct(const std::string& type) override{if (type == "南方香蕉") return new SouthBanana;if (type == "南方苹果") return new SouthApple;if (type == "南方橙子") return new SouthOrange;if (type == "南方西瓜") return new SouthWatermelon;if (type == "南方樱桃") return new SouthCherry;if (type == "南方桃子") return new SouthPeach;if (type == "南方梨") return new SouthPear;return nullptr;}
};void doWorking()
{Fruit		*fruit = nullptr;AbstractFactory 	*abstractfactory = nullptr;// 注册南方工厂abstractfactory = new SouthFactory;fruit = abstractfactory->CreateProduct("南方香蕉");if (fruit) { fruit->getProduct(); delete fruit; }fruit = abstractfactory->CreateProduct("南方苹果");if (fruit) { fruit->getProduct(); delete fruit; }fruit = abstractfactory->CreateProduct("南方橙子");if (fruit) { fruit->getProduct(); delete fruit; }fruit = abstractfactory->CreateProduct("南方西瓜");if (fruit) { fruit->getProduct(); delete fruit; }fruit = abstractfactory->CreateProduct("南方樱桃");if (fruit) { fruit->getProduct(); delete fruit; }fruit = abstractfactory->CreateProduct("南方桃子");if (fruit) { fruit->getProduct(); delete fruit; }fruit = abstractfactory->CreateProduct("南方梨");if (fruit) { fruit->getProduct(); delete fruit; }// 注销南方工厂delete abstractfactory;// 注册北方工厂abstractfactory = new NorthFactory;fruit = abstractfactory->CreateProduct("北方香蕉");if (fruit) { fruit->getProduct(); delete fruit; }fruit = abstractfactory->CreateProduct("北方苹果");if (fruit) { fruit->getProduct(); delete fruit; }fruit = abstractfactory->CreateProduct("北方橙子");if (fruit) { fruit->getProduct(); delete fruit; }fruit = abstractfactory->CreateProduct("北方西瓜");if (fruit) { fruit->getProduct(); delete fruit; }fruit = abstractfactory->CreateProduct("北方樱桃");if (fruit) { fruit->getProduct(); delete fruit; }fruit = abstractfactory->CreateProduct("北方桃子");if (fruit) { fruit->getProduct(); delete fruit; }fruit = abstractfactory->CreateProduct("北方梨");if (fruit) { fruit->getProduct(); delete fruit; }// 注销北方工厂delete abstractfactory;return ;
}int main(){//开工干活doWorking();return 0;
}

运行效果

  1. 抽象工厂类 (AbstractFactory): 这是一个抽象类,定义了一个抽象方法 CreateProduct,用于创建不同类型的 Fruit 对象。

  2. 具体工厂类 (NorthFactory 和 SouthFactory): 这些类继承自 AbstractFactory,实现了 CreateProduct 方法。它们负责生产具体的 Fruit 对象,如 NorthBananaSouthApple 等。

  3. 抽象产品类 (Fruit): 这是一个抽象基类,定义了一个纯虚方法 getProduct,由具体的产品类实现。

  4. 具体产品类 (NorthBananaSouthApple 等): 这些类继承自 Fruit,实现了 getProduct 方法,代表了具体的水果产品。

  5. 客户端代码 (doWorking 函数): 客户端代码通过抽象工厂接口创建对象,而不关心具体的实现类。它可以轻松地切换工厂(比如从南方工厂切换到北方工厂),而无需修改对象创建的代码。

在代码中,AbstractFactory 提供了创建 Fruit 对象的接口,而 NorthFactory 和 SouthFactory 分别实现了这个接口,创建北方和南方的水果。这样,如果需要添加新的工厂(比如中方工厂)或新的水果类型,只需要在相应的工厂类中添加相应的代码,而不需要修改客户端代码。

 四、总结

抽象工厂模式通过提供一个创建一系列相关对象的接口,解耦了客户端代码和具体产品类,使得系统具有更好的灵活性和扩展性。它广泛应用于各种软件系统中,尤其是需要创建和管理多种相关对象的场景。


http://www.ppmy.cn/news/1507258.html

相关文章

Python:协程 - 快速创建三步骤

asyncio 协程快速创建三步骤&#xff1a; 1、编写协程函数。 2、创建协程对象。 3、加入事件循环。 import asyncioasync def do_somethings():# 1、定义协程函数print(1)await asyncio.sleep(0.5)async def do_another():print(2)await asyncio.sleep(0.2)async def do_other…

haproxy七层代理

目录 一、haproxy简介 二、haproxy实验 1.环境部署 2.haproxy的基本部署方法及负载均衡的实现 2.1安装软件 2.2haproxy的基本配置 3.haproxy的全局配置参数及日志分离 3.1多线程设定 3.2自定义日志 4.haproxy-proxies中的常用配置参数 4.1设置backup --- sorryserver…

TCP如何建立长连接

文章目录 TCP建立长连接长连接和短连接长连接的优势TCP KEEPALIVE 心跳包心跳检测步骤 断线重连断线重连函数实现 实例服务端客户端程序功能演示效果 TCP建立长连接 长连接和短连接 长连接是指不论TCP的客户端和服务器之间是否有数据传输&#xff0c;都保持建立的TCP连接&…

CSS优先级,没你想的那么简单!全面介绍影响CSS优先级的各类因素

简介 CSS的中文名称叫做“层叠样式表”&#xff0c;其中的层叠就是指根据各类优先级规则来处理冲突的样式。层叠是CSS的一个重要特性&#xff0c;优先级也是CSS学习中一项非常重要的内容。 提到CSS优先级&#xff0c;我们首先会想到各类的选择器&#xff0c;例如ID选择器&…

学习记录——day28 信号量集

目录 一、信号量集 1、信号量集的API函数接口 二、 将信号量集函数再次封装 1、sem.h 2、sem.c 三、使用信号量集完成共享内存的进程同步 1、发送端 2、接收端 一、信号量集 信号量集&#xff0c;其实就是无名信号量的集合&#xff0c;主要用于完整多个进程间的同步问题.…

127. Go反射基本原理

文章目录 反射基础 - go 的 interface 是怎么存储的&#xff1f;iface 和 eface 的结构体定义&#xff08;runtime/iface.go&#xff09;&#xff1a;_type 是什么&#xff1f;itab 是什么&#xff1f; 反射对象 - reflect.Type 和 reflect.Value反射三大定律Elem 方法reflect.…

【数据结构】三、栈和队列:6.链队列、双端队列、队列的应用(树的层次遍历、广度优先BFS、先来先服务FCFS)

文章目录 2.链队列2.1初始化&#xff08;带头结点&#xff09;不带头结点 2.2入队&#xff08;带头结点&#xff09;2.3出队&#xff08;带头结点&#xff09;❗2.4链队列c实例 3.双端队列考点:输出序列合法性栈双端队列 队列的应用1.树的层次遍历2.图的广度优先遍历3.操作系统…

【Kubernetes】Service 概念与实战

Service 概念与实战 1.通过 Service 向外部暴露 Pod2.Service 的多端口设置3.集群内部的 DNS 服务4.无头 Service 在 Kubernetes 中部署的应用可能对应一个或者多个 Pod&#xff0c;而每个 Pod 又具有独立的 IP 地址。Service&#xff08;服务&#xff09;能够为一组功能相同的…

大数据-72 Kafka 高级特性 稳定性-事务 (概念多枯燥) 定义、概览、组、协调器、流程、中止、失败

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…

Linux中安装MYSQL数据库

文章目录 一、MYSQL数据库介绍1.1、MySQL数据库的基本概述1.2、MySQL数据库的主要特性1.3、MySQL数据库的技术架构与组件1.4、MySQL数据库的应用与扩展性1.5、MySQL数据库的许可模式与开源生态 二、MySQL Workbench和phpMyAdmin介绍2.1、MySQL Workbench介绍2.2、phpMyAdmin介绍…

【学习笔记】Day 9

一、进度概述 1、inversionnet_train 试运行——成功 二、详情 1、inversionnet_train 试运行 在经历了昨天的事故后&#xff0c;今天最终成功运行了 inversionnet_train&#xff0c;运行结果如下&#xff1a; 经观察&#xff0c;最开始 loss 值大概为 0.5 左右 随着训练量的增…

使用Selenium调试Edge浏览器的常见问题与解决方案

背景介绍 在当今互联网时代&#xff0c;网页爬虫已经成为数据获取的重要手段。而Selenium作为一款功能强大的自动化测试工具&#xff0c;被广泛应用于网页爬取任务中。虽然Chrome浏览器是Selenium用户的常见选择&#xff0c;但在某些工作环境中&#xff0c;我们可能需要使用Ed…

Ubuntu24.04设置国内镜像软件源

参考文章&#xff1a; Ubuntu24.04更换源地址&#xff08;新版源更换方式&#xff09; - 陌路寒暄 一、禁用原来的软件源 Ubuntu24.04 的源地址配置文件发生改变&#xff0c;不再使用以前的 sources.list 文件&#xff0c;升级 24.04 之后&#xff0c;该文件内容变成了一行注…

牛客-热身小游戏

题目链接&#xff1a;热身小游戏 第一种写法&#xff1a;线段树 介绍第二种写法&#xff1a;并查集 对于一些已经查询过的点&#xff0c;我们可以往后跳&#xff0c;进行路径压缩&#xff0c;他们的父亲为下一个点。 a数组记录[ l , r ] 之间的乘积&#xff0c;初始值为1。…

haproxy知识点整理

haproxy知识点整理 haproxy七层代理负载均衡什么是负载均衡为什么使用负载均衡 负载均衡类型四层负载均衡七层负载均衡四层和七层的区别 环境搭建:客户端(client)haproxy服务器两台服务器hapserver1hapserver2 简单的haproxy负载均衡 haproxy的基本配置信息global配置proxies配…

17. ADC开发

1. 概述 bes2700 支持2路ADC 2. 硬件连接 3. 软件开发 电压值计算:电压 = 参考电压/4096(2的12次方) * ADC值

linux中安装nginx方法

1、首先确保系统已经安装gcc&#xff0c;如没安装&#xff0c;请先自行安装 2、安装nginx 将openssl-1.1.1j.tar.gz、pcre-8.44.tar.gz、zlib-1.3.tar.gz、nginx-1.20.0.tar.gz解压到当前目录&#xff0c;命令如下&#xff1a; tar -zxvf openssl-1.1.1j.tar.gz tar -zxvf…

【RISC-V设计-08】- RISC-V处理器设计K0A之BMU

【RISC-V设计-08】- RISC-V处理器设计K0A之BMU 文章目录 【RISC-V设计-08】- RISC-V处理器设计K0A之BMU1.简介2.顶层设计3.端口说明4.总线时序4.1 总线写时序4.2 总线读时序 5.代码设计6.总结 1.简介 总线管理单元&#xff08;Bus Management Unit&#xff0c;简称 BMU&#x…

Linux安全与高级应用(四)深入探索MySQL数据库:安装、管理与安全实践

文章目录 标题&#xff1a;全面解析LAMP平台部署及应用第一部分&#xff1a;LAMP平台概述第二部分&#xff1a;准备工作第三部分&#xff1a;安装和配置PHP第四部分&#xff1a;配置Apache第五部分&#xff1a;测试LAMP平台第六部分&#xff1a;部署phpMyAdmin总结 &#x1f44…

【海贼王航海日志:前端技术探索】CSS你了解多少?(三)

目录 1 -> 浏览器调试工具——查看CSS属性 1.1 -> 打开浏览器 1.2 -> 标签页含义 1.3 -> elements标签页使用 2 -> 元素的显示模式 2.1 -> 块级元素 2.2 -> 行内元素/内联元素 2.3 -> 改变显示模式 3 -> 盒模型 3.1 -> 边框 3.2 ->…