设计模式——抽象工厂模式(Abstract Factory Pattern)

概述

       抽象工厂模式的基本思想是将一些相关的产品组成一个“产品族”,由同一个工厂统一生产。在工厂方法模式中具体工厂负责生产具体的产品,每一个具体工厂对应一种具体产品,工厂方法具有唯一性,一般情况下,一个具体工厂中只有一个或者一组重载的工厂方法。但是有时候我们希望一个工厂可以提供多个产品对象,而不是单一的产品对象,如一个电器工厂,它可以生产电视机、电冰箱、空调等多种电器,而不是只生产某一种电器。为了更好地理解抽象工厂模式,我们先引入两个概念:

       (1) 产品等级结构:产品等级结构即产品的继承结构,如一个抽象类是电视机,其子类有海尔电视机、海信电视机、TCL电视机,则抽象电视机与具体品牌的电视机之间构成了一个产品等级结构,抽象电视机是父类,而具体品牌的电视机是其子类。

       (2) 产品族:在抽象工厂模式中,产品族是指由同一个工厂生产的,位于不同产品等级结构中的一组产品,如海尔电器工厂生产的海尔电视机、海尔电冰箱,海尔电视机位于电视机产品等级结构中,海尔电冰箱位于电冰箱产品等级结构中,海尔电视机、海尔电冰箱构成了一个产品族。

       产品等级结构与产品族示意图如图所示:

       当系统所提供的工厂生产的具体产品并不是一个简单的对象,而是多个位于不同产品等级结构、属于不同类型的具体产品时就可以使用抽象工厂模式。抽象工厂模式是所有形式的工厂模式中最为抽象和最具一般性的一种形式。抽象工厂模式与工厂方法模式最大的区别在于,工厂方法模式针对的是一个产品等级结构,而抽象工厂模式需要面对多个产品等级结构,一个工厂等级结构可以负责多个不同产品等级结构中的产品对象的创建。当一个工厂等级结构可以创建出分属于不同产品等级结构的一个产品族中的所有对象时,抽象工厂模式比工厂方法模式更为简单、更有效率。抽象工厂模式示意图如下图所示:

       在上图中,每一个具体工厂可以生产属于一个产品族的所有产品,例如生产颜色相同的正方形、圆形和椭圆形,所生产的产品又位于不同的产品等级结构中。如果使用工厂方法模式,上图所示结构需要提供15个具体工厂,而使用抽象工厂模式只需要提供5个具体工厂,极大减少了系统中类的个数。

      抽象工厂模式(Abstract Factory Pattern):提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。抽象工厂模式又称为Kit模式,它是一种对象创建型模式。在抽象工厂模式中,每一个具体工厂都提供了多个工厂方法用于产生多种不同类型的产品,这些产品构成了一个产品族,抽象工厂模式结构如图所示:

在抽象工厂模式结构图中包含如下几个角色:

       ● AbstractFactory(抽象工厂):它声明了一组用于创建一族产品的方法,每一个方法对应一种产品。

       ● ConcreteFactory(具体工厂):它实现了在抽象工厂中声明的创建产品的方法,生成一组具体产品,这些产品构成了一个产品族,每一个产品都位于某个产品等级结构中。

       ● AbstractProduct(抽象产品):它为每种产品声明接口,在抽象产品中声明了产品所具有的业务方法。

       ● ConcreteProduct(具体产品):它定义具体工厂生产的具体产品对象,实现抽象产品接口中声明的业务方法。

简单实现

       以生产各种颜色的桌椅为例,桌子、椅子为一组产品族,而每种家具的颜色作为产品等级(类似于不同颜色的烤漆房)。

       在抽象工厂中声明了多个工厂方法,用于创建不同类型的产品,抽象工厂可以是接口,也可以是抽象类或者具体类,其典型代码如下所示:

       具体工厂实现了抽象工厂,每一个具体的工厂方法可以返回一个特定的产品对象,而同一个具体工厂所创建的产品对象构成了一个产品族。对于每一个具体工厂类,其典型代码如下所示:

编写测试类并运行:

       当我们需要生产蓝色家具的时候,不需要更改抽象工厂,只需要增加蓝色工厂类以及两个具体蓝色产品即可,符合“开闭原则”,但是当需要增加一个新产品的时候,例如凳子,就需要修改抽象工厂及所有的具体工厂,违背了“开闭原则”。

       与工厂方法模式一样,抽象工厂模式也可为每一种产品提供一组重载的工厂方法,以不同的方式对产品对象进行创建。

总结

       在抽象工厂模式中,增加新的产品族很方便,但是增加新的产品等级结构很麻烦(即新增一种各个产品等级结构的产品),抽象工厂模式的这种性质称为“开闭原则”的倾斜性。“开闭原则”要求系统对扩展开放,对修改封闭,通过扩展达到增强其功能的目的,对于涉及到多个产品族与多个产品等级结构的系统,其功能增强包括两方面:

       (1) 增加产品族:对于增加新的产品族,抽象工厂模式很好地支持了“开闭原则”,只需要增加具体产品并对应增加一个新的具体工厂,对已有代码无须做任何修改。

       (2) 增加新的产品等级结构:对于增加新的产品等级结构,需要修改所有的工厂角色,包括抽象工厂类,在所有的工厂类中都需要增加生产新产品的方法,违背了“开闭原则”。

       正因为抽象工厂模式存在“开闭原则”的倾斜性,它以一种倾斜的方式来满足“开闭原则”,为增加新产品族提供方便,但不能为增加新产品结构提供这样的方便,因此要求设计人员在设计之初就能够全面考虑,不会在设计完成之后向系统中增加新的产品等级结构,也不会删除已有的产品等级结构,否则将会导致系统出现较大的修改,为后续维护工作带来诸多麻烦。

       抽象工厂模式是工厂方法模式的进一步延伸,由于它提供了功能更为强大的工厂类并且具备较好的可扩展性,在软件开发中得以广泛应用,尤其是在一些框架和API类库的设计中,例如在Java语言的AWT(抽象窗口工具包)中就使用了抽象工厂模式,它使用抽象工厂模式来实现在不同的操作系统中应用程序呈现与所在操作系统一致的外观界面。抽象工厂模式也是在软件开发中最常用的设计模式之一。

1. 主要优点

       (1) 抽象工厂模式隔离了具体类的生成,使得客户并不需要知道什么被创建。由于这种隔离,更换一个具体工厂就变得相对容易,所有的具体工厂都实现了抽象工厂中定义的那些公共接口,因此只需改变具体工厂的实例,就可以在某种程度上改变整个软件系统的行为。

        (2) 当一个产品族中的多个对象被设计成一起工作时,它能够保证客户端始终只使用同一个产品族中的对象。

       (3) 增加新的产品族很方便,无须修改已有系统,符合“开闭原则”。

2. 主要缺点

       抽象工厂模式的主要缺点如下:

       增加新的产品等级结构(新产品)麻烦,需要对原有系统进行较大的修改,甚至需要修改抽象层代码,这显然会带来较大的不便,违背了“开闭原则”。

3. 适用场景

       在以下情况下可以考虑使用抽象工厂模式:

       (1) 一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节,这对于所有类型的工厂模式都是很重要的,用户无须关心对象的创建过程,将对象的创建和使用解耦。

       (2) 系统中有多于一个的产品族,而每次只使用其中某一产品族。可以通过配置文件等方式来使得用户可以动态改变产品族,也可以很方便地增加新的产品族。

       (3) 属于同一个产品族的产品将在一起使用,这一约束必须在系统的设计中体现出来。同一个产品族中的产品可以是没有任何关系的对象,但是它们都具有一些共同的约束,如同一操作系统下的按钮和文本框,按钮与文本框之间没有直接关系,但它们都是属于某一操作系统的,此时具有一个共同的约束条件:操作系统的类型。

       (4) 产品等级结构稳定,设计完成之后,不会向系统中增加新的产品等级结构或者删除已有的产品等级结构。

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

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

相关文章

数据结构与算法之美学习笔记:46 | 概率统计:如何利用朴素贝叶斯算法过滤垃圾短信?

目录 前言算法解析总结引申 前言 本节课程思维导图: 上一节我们讲到,如何用位图、布隆过滤器,来过滤重复的数据。今天,我们再讲一个跟过滤相关的问题,如何过滤垃圾短信? 垃圾短信和骚扰电话,我…

docker安装nodejs,并更改为淘宝源

拉取官方 Node.js 镜像 docker pull node:latest创建 Dockerfile,并更改 NPM 下载源为淘宝源,设置为全局持久化 # 使用最新版本的Node.js作为基础镜像 FROM node:latest# 设置工作目录为/app WORKDIR /app # 更改 NPM 下载源为淘宝源,并设置…

c++学习笔记-STL案例-机房预约系统1-需求分析

1 机房预约系统需求 1.1 简单介绍 学校有几个规格不同的机房,由于使用经常出现“撞车”现象,现在开发一套预约系统,解决这一问题。 1.2 身份介绍 分别有三种身份使用该系统 学生代表:申请使用机房教师:审核学生的…

从单细胞数据分析的最佳实践看R与Python两个阵营的博弈

R与Python,在生物信息学领域的博弈异常激烈。许多生信分析,两个阵营都发展出了自己的方法,比如单细胞数据分析,R有Seurat,Python就有Scanpy。这些层出不穷的方法不断地吸引着吃瓜群众的眼球,同时也让人患上…

Qt 6之六:Qt Designer介绍

Qt 6之六:Qt Designer介绍 Qt Designer是一个可视化的用户界面设计工具,用于创建Qt应用程序的用户界面,允许开发人员通过拖放和布局来设计和创建GUI界面。 Qt 6之一:简介、安装与简单使用 https://blog.csdn.net/cnds123/articl…

Linux 文件(夹)权限查看

命令 : ls -al ls -al 是一个用于列出指定目录下所有文件和子目录的命令,包括隐藏文件和详细信息。其中,-a 选项表示显示所有文件,包括以 . 开头的隐藏文件,-l 选项表示以列表的形式显示文件的详细信息。 本例中:drwxrwxr-x 为权限细节。 权限细节(Permission detail…

DePIN:重塑物理资源网络的未来

点击查看TechubNews更多相关推荐 一、DePIN:物理资源的新整合方式 Depin赛道的项目如雨后春笋般涌现,为市场注入了新的活力。作为先行者,Coinmanlabs已经深入布局Depin赛道,其中最引人注目的项目当属Grass。 什么是DePIN DePIN…

uniapp在web端怎么使用svg图标呢

在图标库中添加好项目用到的图标,点击symbol点击生成在线链接 点击生成的在线链接,此时会跳转到一个新窗口,是一个js文件 复制这个js文件的内容 然后在uniapp中新建svg.js文件,把从上面复制的代码粘贴到这个svg.js中 在main.js中引…

JAVA基础学习笔记-day16-网络编程

JAVA基础学习笔记-day16-网络编程 1. 网络编程概述1.1 软件架构1.2 网络基础 2. 网络通信要素2.1 如何实现网络中的主机互相通信2.2 通信要素一:IP地址和域名2.2.1 IP地址2.2.2 域名 2.3 通信要素二:端口号2.4 通信要素三:网络通信协议 3. 谈…

0110qt

完善对话框,点击登录对话框,如果账号和密码匹配,则弹出信息对话框,给出提示"登录成功",提供一个Ok按钮,用户点击Ok后,关闭登录界面,跳转到其他界面 如果账号和密码不匹配&…

Pytorch基础:数据读取与预处理——图像读取与存储

Pytorch基础:数据读取与预处理——图像读取与存储 1.读取图片2. 使用 matplotlib 库显示和保存图像 1.读取图片 图像库 opencv-python、imageio、PIL 等都具有图像读取的功能。 (base) PS C:\Users\阳> conda activate yang (yang) PS C:\Users\阳> python …

脚本编程游戏引擎会遇到哪些问题

在游戏开发中,脚本编程已经成为了一种非常常见的方式,用来实现游戏逻辑和功能。但是脚本编程游戏引擎也可能会面临一些挑战和问题。下面简单的探讨一下都会遇到哪些问题,并且该如果做。 性能问题 脚本语言通常需要运行时解释执行&#xff0…

【Kafka-3.x-教程】-【六】Kafka 外部系统集成 【Flume、Flink、SpringBoot、Spark】

【Kafka-3.x-教程】专栏: 【Kafka-3.x-教程】-【一】Kafka 概述、Kafka 快速入门 【Kafka-3.x-教程】-【二】Kafka-生产者-Producer 【Kafka-3.x-教程】-【三】Kafka-Broker、Kafka-Kraft 【Kafka-3.x-教程】-【四】Kafka-消费者-Consumer 【Kafka-3.x-教程】-【五…

Pycharm 重命名SSH的conda解释器后,提示该解释器不可用

结论 目前已知是BUG,截至投稿日未修复已经提交给youtrack.jetbrains.com,如下图: 解决方法,待更新…

如何给AVR16芯片解锁

AVRM16核心板本身集成了强大的芯片自解锁功能模块,当由于熔丝位设置错误,导致芯片锁死,无法正常使用时候,可以利用畅学AVR16核心板上的解锁功能给芯片解锁。 (如果芯片没有锁死,可以跳过此步骤&#xff09…

ffmpeg解码音频planar模式和packed模式

转载:原文地址: FFmpeg连载4-音频解码-阿里云开发者社区ffmpeg连载系列https://developer.aliyun.com/article/1197520 导读 前面我们介绍了使用FFmpeg解码视频,今天我们使用FFmpeg解码音频。我们的目标将mp4中的音频文件解码成PCM数据&…

python 文本内容随机生成器

这段代码是一个用于生成指定长度的随机文本的函数。主要包括两个函数:generate_text()和generate_other_content()。 generate_text(original_text, length)函数接受两个参数:原始文本和生成文本的长度。该函数的作用是根据原始文本生成指定长度的文本。…

Java异常及网络编程

异常续 throws关键字 当一个方法中使用throw抛出一个非RuntimeException的异常时,就要在该方法上使用throws声明这个异常的抛出。此时调用该方法的代码就必须处理这个异常,否则编译不通过。 package exception; ​ /*** 测试异常的抛出*/ public clas…

【普中开发板】基于51单片机的简易密码锁设计( proteus仿真+程序+设计报告+讲解视频)

基于51单片机的简易密码锁设计 1.主要功能:资料下载链接: 实物图:2.仿真3. 程序代码4. 设计报告5. 设计资料内容清单 【普中】基于51单片机的简易密码锁设计 ( proteus仿真程序设计报告讲解视频) 仿真图proteus8.16(有低版本) 程…

vue知识-04

计算属性computed 注意: 1、计算属性是基于它们的依赖进行缓存的 2、计算属性只有在它的相关依赖发生改变时才会重新求值 3、计算属性就像Python中的property,可以把方法/函数伪装成属性 4、computed: { ... } 5、计算属性必须要有…