六、抽象工厂模式

文章目录

  • 1 基本介绍
  • 2 案例
    • 2.1 Drink 抽象类
    • 2.2 Tea 类
    • 2.3 Coffee 类
    • 2.4 DrinkFactory 接口
    • 2.5 TeaFactory 类
    • 2.6 CoffeeFactory 类
    • 2.7 Client 类
    • 2.8 Client 类运行结果
    • 2.9 总结
  • 3 各角色之间的关系
    • 3.1 角色
      • 3.1.1 Product ( 抽象产品 )
      • 3.1.2 ConcreteProduct ( 具体产品 )
      • 3.1.3 Factory ( 抽象工厂 )
      • 3.1.4 ConcreteFactory ( 具体工厂 )
      • 3.1.5 Client ( 客户端 )
    • 3.2 类图
  • 4 注意事项
  • 5 在源码中的使用
  • 6 优缺点
  • 7 适用场景
  • 8 总结


1 基本介绍

抽象工厂模式(Abstract Factory Pattern)是一种 创建型 设计模式,它提供了一种 创建一系列相关或相互依赖对象的接口,而 无需指定它们具体的类

它不同于 简单工厂模式,抽象工厂模式将对象的创建逻辑分散到各个具体的工厂类中,而不是集中在一个工厂类中。

它还不同于 工厂方法模式,抽象工厂模式在创建产品对象时没有 相似的额外操作,所以不需要给外界提供一个方法,而强制子类实现另一个方法。

2 案例

本模式的案例 和 简单工厂模式、工厂方法模式 的案例很相似,只不过将其中的工厂改为了 一个抽象工厂接口 + 多个工厂实现类。如下所示:

  • 饮品:Drink 抽象类有一个抽象方法 drink(),代表喝这个饮品。它有两个子类:Tea 类和 Coffee 类。
  • 工厂:DrinkFactory 接口有一个方法,能够根据饮品的类型生产对应的饮品。它有两个实现类,分别为 TeaFactory 类和 CoffeeFactory 类。
  • 客户端:Client 类中使用了不同的饮品工厂,创建了不同类型的饮品。

2.1 Drink 抽象类

public abstract class Drink { // 饮品的抽象类public abstract void drink(); // 喝饮品protected String name; // 饮品的名称public Drink(String name) {this.name = name;}public String getName() {return name;}
}

2.2 Tea 类

public class Tea extends Drink { // 茶饮品public Tea(String name) {super(name);}@Overridepublic void drink() {System.out.println("你喝了[" + super.name + "]这个茶饮品");}
}

2.3 Coffee 类

public class Coffee extends Drink { // 咖啡饮品public Coffee(String name) {super(name);}@Overridepublic void drink() {System.out.println("你喝了[" + super.name + "]这个咖啡饮品");}
}

2.4 DrinkFactory 接口

public interface DrinkFactory  { // 饮品工厂接口Drink createDrink(String drinkName); // 创建 指定名称 的饮品
}

2.5 TeaFactory 类

public class TeaFactory implements DrinkFactory { // 茶饮品工厂类@Overridepublic Drink createDrink(String drinkName) {return new Tea(drinkName); // 创建 茶饮品 的对象}
}

2.6 CoffeeFactory 类

public class CoffeeFactory implements DrinkFactory { // 咖啡饮品工厂类@Overridepublic Drink createDrink(String drinkName) {return new Coffee(drinkName); // 创建 咖啡饮品 的对象}
}

2.7 Client 类

public class Client { // 使用 具体饮品工厂创建具体饮品 的客户端public static void main(String[] args) {DrinkFactory teaFactory = new TeaFactory();Drink tea = teaFactory.createDrink("铁观音");tea.drink();DrinkFactory coffeeFactory = new CoffeeFactory();Drink coffee = coffeeFactory.createDrink("拿铁");coffee.drink();}
}

2.8 Client 类运行结果

你喝了[铁观音]这个茶饮品
你喝了[拿铁]这个咖啡饮品

2.9 总结

在本模式的案例中,工厂类比 简单工厂模式 多;在初始化工厂时,需要初始化具体工厂的对象;在生产产品时,只需要传入与对象有关的参数,不需要传入与对象类型有关的信息。

如果想要新增一种具体产品,则只需写 具体产品类其对应的具体工厂类 的代码,而不需要修改 抽象工厂类,这样就 遵守了开闭原则提高了系统的扩展性

此外,本模式还有简单工厂模式的优点——降低系统之间的耦合度,这是因为在本模式中,客户端也不需要知道具体产品的创建逻辑,只要会使用工厂即可。

3 各角色之间的关系

3.1 角色

3.1.1 Product ( 抽象产品 )

该角色负责 定义 所有具体产品的 共性(包括 属性 和 方法),是所有具体产品的 父类 或 被实现的接口。在本案例中,Drink 抽象类扮演了这个角色。

3.1.2 ConcreteProduct ( 具体产品 )

该角色负责 实现 抽象产品所定义的 方法。在本案例中,Tea 类和 Coffee 类都在扮演这个角色。

3.1.3 Factory ( 抽象工厂 )

该角色负责 定义 创建产品对象的 接口。在本案例中,DrinkFactory 接口扮演了这个角色。

3.1.4 ConcreteFactory ( 具体工厂 )

该角色负责 实现 创建产品对象的 接口,而且只负责创建某一种具体产品。在本案例中,TeaFactory 类和 CoffeeFactory 类都在扮演这个角色。

3.1.5 Client ( 客户端 )

该角色负责 使用 具体工厂创建具体产品。在本案例中,Client 类扮演了这个角色。

3.2 类图

alt text
说明:ConcreteProduct 类和 ConcreteFactory 类是一一对应的,它们两个的关系和 Product 抽象类与 Factory 接口的关系是一致的。

4 注意事项

本模式的注意事项与 工厂方法模式 的十分相似,共有以下几点:

  • 考虑系统复杂度:使用抽象工厂模式需要引入一个或多个工厂类,这可能会增加系统的复杂度,在决定是否使用此模式时,需要权衡其带来的好处和增加的复杂度。
  • 共同基类或接口:所有产品类必须有一个共同的基类或接口,以便 createProduct() 方法能够返回一个 通用 的产品类型,这有助于保持系统的灵活性和可扩展性。
  • 对象重用:在某些情况下,为了 提高性能减少资源消耗createProduct() 方法可能会返回 缓存的对象对象池中的对象,而不是每次都创建新对象,这需要根据具体的应用场景和需求来决定。
  • 扩展具体产品:当需要新增产品时,只需新增产品类和相应的工厂类。
  • 考虑使用接口:在可能的情况下,将抽象产品角色定义为 接口 而不是 抽象类,可以提高系统的灵活性和扩展性。在 Java 中,一个类只能继承一个父类,但能实现多个接口。
  • 考虑使用工厂方法模式:如果在创建产品对象时没有相似的额外操作,则使用本模式比较好;如果有相似的额外操作,最好使用 工厂方法模式。

5 在源码中的使用

在 Spring 框架中,工厂模式被广泛应用于对象的创建和管理过程中。

Spring 的 BeanFactory 接口是抽象工厂模式的典型应用,它 定义 了多个 getBean() 方法,允许客户端通过传入不同的条件(如 Bean 的名称或类型)来获取对应的 Bean 实例。

// 在 org.springframework.beans.factory.BeanFactory 接口中有如下三个方法
Object getBean(String name) throws BeansException;
<T> T getBean(String name, Class<T> requiredType) throws BeansException;
<T> T getBean(Class<T> requiredType) throws BeansException;

这三个方法都没有被实现,只是被定义在 BeanFactory 接口中 ,具体实现交给其实现类,比如 AbstractBeanFactory 抽象类。

6 优缺点

优点

  • 扩展性好:当需要新增产品时,只需新增产品类和相应的工厂类,无需修改原有的工厂接口和工厂类,满足开闭原则。
  • 职责单一:每个工厂类只负责创建对应的产品,满足单一职责原则。
  • 降低耦合度:客户端通过工厂接口与具体产品解耦,不需要知道产品的具体实现细节,只需要知道如何获取所需的产品。
  • 灵活性高:可以根据不同的应用场景或条件选择不同的工厂实现类来创建产品,增加了系统的灵活性。

缺点

  • 增加了系统的复杂度:每增加一种产品,就需要增加一个对应的产品类和一个对应的工厂实现类,这会增加系统中类的数量,从而增加系统的复杂度。
  • 增加了系统的抽象层次:抽象工厂模式引入了 工厂接口 和 工厂实现类,增加了系统的抽象层次,使得系统的理解和维护变得 相对 复杂。
  • 过度设计:在某些情况下,如果系统中产品的 种类不多,或者产品之间的 差异性不大,使用抽象工厂模式可能会导致过度设计,增加了不必要的复杂性。这时使用 简单工厂模式 会好一点。
  • 依赖性问题:如果工厂类依赖于其他资源或配置,那么这些依赖项的管理和配置可能会变得复杂。特别是在大型系统中,工厂类的依赖关系可能会形成一个复杂的网络,增加了系统的维护难度。

7 适用场景

  • 多种产品族的创建:当系统中存在多个产品族,并且这些产品族之间存在公共的接口或抽象类,但每个产品族的具体实现不同时,可以使用工厂方法模式。每个产品族对应一个工厂类,负责创建该族中的具体产品对象。
  • 系统需要扩展但不想修改已有代码:当系统需要添加新的产品类型时,如果希望在不修改已有代码的基础上实现扩展,工厂方法模式是一个很好的选择。
  • 复杂的对象创建:当对象的创建过程比较复杂,包含多个步骤或者需要依赖其他对象时,使用工厂方法模式可以将对象的创建过程封装在工厂类中,使得客户端代码更加简洁和易于理解。
  • 隐藏具体产品的创建细节:在某些情况下,我们可能不希望客户端代码知道具体产品的创建细节,或者希望 隐藏 这些细节以简化客户端的使用。工厂方法模式可以通过提供一个共同的接口来隐藏具体产品的创建细节,使得客户端只需通过接口来创建对象。
  • 使用第三方类库:当系统需要使用第三方类库来创建对象,并且这些对象的创建过程比较复杂或者需要遵循特定的规则时,可以使用工厂方法模式来封装这些创建过程,以便在系统中更加方便地使用这些对象。
  • 设计考虑:在系统设计初期,如果预见到将来可能需要添加新的产品类型或者对产品类型进行扩展,可以考虑使用工厂方法模式来为未来可能的扩展 预留接口。这样可以使得系统在扩展时更加灵活和方便。

8 总结

抽象工厂模式是一种 创建型 设计模式,它通过多个具体工厂类来创建具体产品,这些具体产品通常具有共同的抽象产品作为父类或接口,这些具体工厂类也有共同的抽象工厂作为 接口

抽象工厂模式将具体产品的创建逻辑 封装 在各个工厂类中,客户端无需知道具体产品的类名,只需要知道相应的工厂即可,从而 降低了 客户端与具体产品类之间的 耦合度

与简单工厂模式相比,抽象工厂模式对具体产品的 种类不做限制,并且具体产品的 创建逻辑也可以很复杂 。在扩充产品类型时,该模式不需要修改代码,只需要添加代码,满足 开闭原则。每个具体工厂类只负责创建一种产品,满足 单一职责原则

与工厂方法模式相比,抽象工厂模式 没有处理创建产品对象时相似的额外逻辑,但是在 Java 中,由于单继承,所以在 灵活性 方面,使用接口(抽象工厂模式) 比 使用抽象类(工厂方法模式) 好一些

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

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

相关文章

昇思25天学习打卡营第1天|快速入门-实现一个简单的深度学习模型

目录 实验环境 Jupyter云上开发环境使用 导包 处理数据集 网络构建 模型训练 评估模型性能 保存模型 加载模型 预测推理 实验环境 02-快速入门.ipynb (4) - JupyterLab (mindspore.cn) 规格&#xff1a;4u 16G 20G 镜像&#xff1a;py39-ms2.3.0rc1 特性&#xff1…

SpringBoot 最大连接数及最大并发数是多少

SpringBoot 最大连接数及最大并发数 Spring Boot 是一个基于 Spring 框架的快速开发框架&#xff0c;它本身并不直接管理数据库连接或网络连接的最大连接数和最大并发数。这些参数通常由底层的基础设施和组件来控制&#xff0c;例如&#xff1a; 数据库连接池&#xff1a;Spri…

Qt 使用视口和窗口作图

物理坐标系与逻辑坐标系 绘图设备的物理坐标系是基本的坐标系&#xff0c;通过 QPainter 的平移、旋转等坐标变换可以得到更容 易操作的逻辑坐标系。 物理坐标系也称为视口&#xff08;viewport&#xff09;坐标系&#xff0c;逻辑坐标系也称为窗口&#xff08; window&…

netty使用redis发布订阅实现消息推送

netty使用redis发布订阅实现消息推送 场景 项目中需要给用户推送消息: 接口 RestController public class PushApi {Autowiredprivate PushService pushService;/*** 消息推送* param query* return*/PostMapping("/push/message")public String push(RequestBody…

Linux gcc/g++ _ make/makefile

文章目录 库gcc/g程序编译过程链接动态链接静态链接 make _ makefile 库 一、 什么是库&#xff1f; 库是程序代码的集合&#xff0c;是共享程序代码的一种方式。根据源代码的公开情况&#xff0c;库可以分为两种类型&#xff1a; 开源库&#xff0c;公开源代码&#xff0c;能…

Godot入门 03世界构建1.0版

在game场景&#xff0c;删除StaticBody2D节点&#xff0c;添加TileMap节点 添加TileSet图块集 添加TileSet源 拖动图片到图块&#xff0c;自动创建图块 使用橡皮擦擦除。取消橡皮擦后按住Shift创建大型图块。 进入选择模式&#xff0c;TileMap选择绘制&#xff0c;选中图块后在…

MSQP Mysql数据库权限提升工具,UDF自动检测+快速反向SHELL

项目地址:https://github.com/MartinxMax/MSQP MSQP 这是一个关于Mysql的权限提升工具 安装依赖 $ python3 -m pip install mysql-connector-python 使用方法 $ python3 msqp.py -h 权限提升:建立反向Shell 在建立反向连接前,该工具会自动检测是否具有提权条件&#xff0…

软考:软件设计师 — 7.软件工程

七. 软件工程 1. 软件工程概述 &#xff08;1&#xff09;软件生存周期 &#xff08;2&#xff09;软件过程 软件开发中所遵循的路线图称为 "软件过程"。 针对管理软件开发的整个过程&#xff0c;提出了两个模型&#xff1a;能力成熟度模型&#xff08;CMM&#…

解析嵌入式世界里中断和异常的不同之处

在开始前刚好我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「嵌入式的资料从专业入门到高级教程」&#xff0c;点个关注在评论区回复“666”之后私信回复“666”&#xff0c;全部无偿共享给大家&#xff01;&#xff01;&#xff01;是指CPU内部出现的中断&…

linux命令更新-文本处理awk

awk命令简介 awk是一种强大的文本处理工具&#xff0c;可以对文本文件进行格式化、统计、计算等操作。它逐行读取文本文件&#xff0c;并对每一行进行处理。awk的语法相对简单&#xff0c;但功能非常强大&#xff0c;是Linux系统中常用的文本处理工具之一。 awk命令基本语法 …

超燃!纯AI生成《泰坦尼克号》大片!浙大阿里发布MovieDreamer:超长电影生成“梦工厂“

论文链接&#xff1a;https://arxiv.org/pdf/2407.16655 项目主页&#xff1a;https://aim-uofa.github.io/MovieDreamer/ github链接&#xff1a;https://github.com/aim-uofa/MovieDreamer 亮点直击 MovieDreamer&#xff0c;一个新颖的分层框架&#xff0c;将自回归模型与扩…

idea设置类注释模板作者、日期、描述等信息

文章目录 前言一、新建类的时候自动添加类注释1.打开设置2.模版配置示例如下3.实际生成效果 前言 由于每次换电脑时都需要重新对idea进行设置&#xff0c;为了方便大家的开发配置&#xff0c;同时也为自己以后配置留一份记录&#xff08;毕竟每次换环境都需要重新配置一遍&…

代码审计 | .NET SqlSugar框架注入漏洞

01阅读须知 此文所节选自小报童《.NET 代码审计》专栏&#xff0c;主要内容有涉及的.NET目录和文件操作、SQL注入方向的敏感函数、还有不安全的配置导致的漏洞挖掘思路&#xff0c;对.NET代码审计感兴趣的朋友们可以解锁该电子报刊&#xff0c;解锁更多的报刊内容。 02基本介…

【Unity】 HTFramework框架(五十三)使用 Addressables 可寻址系统

更新日期&#xff1a;2024年7月25日。 Github源码&#xff1a;[点我获取源码] Gitee源码&#xff1a;[点我获取源码] 索引 Addressables 可寻址系统使用 Addressables 可寻址系统一、导入 Addressables二、切换到 Addressables 加载模式三、切换资源加载助手四、加载资源五、注…

C++基础知识:函数重载是什么?函数重载基础用法,函数重载的作用,函数重载满足条件(关键),注意事项,函数重载代码语法的函数

1.作用&#xff1a; 函数名可以相同&#xff0c;提高复用性 2.函数重载满足条件&#xff08;关键&#xff09;&#xff1a; 1.同一个作用域下 2.函数名称相同 3.函数参数类型不同 或者 个数不同 或者 顺序不同 注意&#xff1a;函数的返回值不可以作为函数重载的条件 1.常见…

【python】python销售数据分析可视化(源码+论文+数据集)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

CTF-pwn-虚拟化-vmmware 前置

文章目录 参考vmware逃逸简介虚拟机和主机通信机制(guest to host)共享内存&#xff08;弃用&#xff09;backdoor机制Message_Send和Message_RecvGuestRPC实例RpcOutSendOneRawWork实例 vmware-rpctool info-get guestinfo.ip各个步骤对应的backdoor操作Open RPC channelSend …

【MySQL进阶之路 | 高级篇】页锁+锁的思想(悲观锁和乐观锁)

1. 页锁 页锁就是在页的粒度上进行锁定&#xff0c;锁定的数据资源比行锁要多&#xff0c;因为一个页中可以有多个行记录.当我们使用页锁的时候&#xff0c;会出现数据浪费的现象&#xff0c;但这样的浪费最多也就是一个页上的数据行。页锁的开销介于表锁和行锁之间&#xff0…

Internet Download Manager2024功能特点优势分析及使用建议及注意事项

1. Internet Download Manager简介 2. 功能特点 3. 优势分析 4. 专家评价 5. 使用建议及注意事项 6. 常见问题解答 7. 用户反馈及案例分享 8. IDM下载器的未来发展趋势 文章&#xff1a; 在互联网快速发展的今日&#xff0c;人们对于网络资源的获取和利用越来越频繁。无论…

【Python面试题收录】Python编程基础练习题①(数据类型+函数+文件操作)

本文所有代码打包在Gitee仓库中https://gitee.com/wx114/Python-Interview-Questions 一、数据类型 第一题&#xff08;str&#xff09; 请编写一个Python程序&#xff0c;完成以下任务&#xff1a; 去除字符串开头和结尾的空格。使用逗号&#xff08;","&#…