Java 设计模式——抽象工厂模式
目录
- 1.概念
- 2.结构
- 3.实现
- 4.优缺点
- 5.使用场景
- 6.模式扩展
- 7.JDK源码解析——Collection.iterator方法
1.概念
(1)Java 设计模式——工厂方法模式中考虑的是一类产品的生产,如畜牧场只养动物、电视机厂只生产电视机等。这些工厂只生产同种类产品,同种类产品称为同等级产品,也就是说:工厂方法模式只考虑生产同等级的产品,但是在现实生活中许多工厂是综合型的工厂,能生产多等级(种类) 的产品,如电器厂既生产电视机又生产洗衣机或空调,大学既有软件专业又有生物专业等。
(2)本文要介绍的抽象工厂模式将考虑多等级产品的生产,将同一个具体工厂所生产的位于不同等级的一组产品称为一个产品族,下图所示横轴是产品等级,也就是同一类产品;纵轴是产品族,也就是同一品牌的产品,同一品牌的产品产自同一个工厂。具体来说,抽象工厂模式是一种为访问类提供一个创建一组相关或相互依赖对象的接口,且访问类无须指定所要产品的具体类就能得到同族的不同等级的产品的模式结构。
(3)抽象工厂模式是工厂方法模式的升级版本,它们之间有以下几个区别:
- 粒度不同:抽象工厂模式关注一族相关对象的创建,一个工厂负责创建一族产品对象;而工厂方法模式关注单个对象的创建,每个工厂只负责创建一种产品。
- 抽象程度不同:抽象工厂模式具有更高的层次和更大的封装性,它通过引入抽象工厂和具体工厂的概念,将一族产品对象的创建交给抽象工厂来完成;而工厂方法模式的抽象程度相对较低,它通过定义一个工厂接口或抽象类来声明创建产品的方法,然后具体工厂类会实现这个接口或抽象类来创建具体产品对象。
- 关注点不同:抽象工厂模式关注的是一族产品对象的创建,它解决的是多个产品对象之间的组合问题,确保一族产品对象能够相互协作;而工厂方法模式关注的是单个产品对象的创建,它解决的是产品扩展和变化的问题。
- 扩展性不同:抽象工厂模式的扩展性更强,可以同时添加新的具体工厂和产品类,以及扩展一族产品的组合方式;而工厂方法模式的扩展性相对较低,当需要添加新的产品时,需要新增对应的具体工厂类和具体产品类。
综上所述,抽象工厂模式和工厂方法模式在粒度、抽象程度、关注点和扩展性等方面存在差异。选择使用哪种模式取决于具体的业务需求和设计要求。如果需要创建一族相关的产品对象,并确保这些产品对象能够相互协作,可以考虑使用抽象工厂模式;如果只需要创建单个产品对象,并且希望能够轻松扩展和添加新的产品类,可以选用工厂方法模式。
2.结构
抽象工厂模式的主要角色如下:
- 抽象工厂 (Abstract Factory):提供了创建产品的接口,它包含多个创建产品的方法,可以创建多个不同等级的产品。
- 具体工厂 (Concrete Factory):主要是实现抽象工厂中的多个抽象方法,完成具体产品的创建。
- 抽象产品 (Product):定义了产品的规范,描述了产品的主要特性和功能,抽象工厂模式有多个抽象产品。
- 具体产品 (ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间是多对一的关系。
3.实现
现咖啡店业务发生改变,不仅要生产咖啡还要生产甜点,如提拉米苏、抹茶慕斯等,要是按照工厂方法模式,需要定义提拉米苏类、抹茶慕斯类、提拉米苏工厂、抹茶慕斯工厂、甜点工厂类,很容易发生类爆炸情况。其中拿铁咖啡、美式咖啡是一个产品等级,都是咖啡;提拉米苏、抹茶慕斯也是一个产品等级;拿铁咖啡和提拉米苏是同一产品族(也就是都属于意大利风味),美式咖啡和抹茶慕斯是同一产品族(也就是都属于美式风味)。所以这个案例可以使用抽象工厂模式实现。类图如下:
部分核心代码如下:
抽象工厂:DessertFactory.java
package com.itheima.patterns.factory.abstract_factory;//抽象工厂类
public interface DessertFactory {//生产咖啡的功能Coffee createCoffee();//生产甜品的功能Dessert createDessert();
}
具体工厂:AmericanDessertFactory.java
package com.itheima.patterns.factory.abstract_factory;//美式风味的甜品工厂,可以生产美式咖啡和抹茶慕斯
public class AmericanDessertFactory implements DessertFactory{@Overridepublic Coffee createCoffee() {return new AmericanCoffee();}@Overridepublic Dessert createDessert() {return new Matchamousse();}
}
具体工厂:ItalyDessertFactory.java
package com.itheima.patterns.factory.abstract_factory;//意大利风味甜品工厂,可以生产拿铁咖啡和提拉米苏甜品
public class ItalyDessertFactory implements DessertFactory{@Overridepublic Coffee createCoffee() {return new LatteCoffee();}@Overridepublic Dessert createDessert() {return new Trimisu();}
}
Client.java
package com.itheima.patterns.factory.abstract_factory;public class Client {public static void main(String[] args) {//创建的是意大利风味甜品工厂对象ItalyDessertFactory factory = new ItalyDessertFactory();//获取拿铁咖啡和提拉米苏甜品Coffee coffee = factory.createCoffee();Dessert dessert = factory.createDessert();System.out.println(coffee.getName());dessert.show();}
}
输出结果如下:
public class Client {public static void main(String[] args) {//创建的是意大利风味甜品工厂对象ItalyDessertFactory factory = new ItalyDessertFactory();//获取拿铁咖啡和提拉米苏甜品Coffee coffee = factory.createCoffee();Dessert dessert = factory.createDessert();System.out.println(coffee.getName());dessert.show();}
}
如果要加同一个产品族的话,只需要再加一个对应的工厂类即可,不需要修改其他的类。
4.优缺点
(1)优点:
- 封装性强:抽象工厂模式将一族相关产品的创建逻辑封装在一个工厂中,客户端通过抽象工厂来创建产品对象,无需关心具体的产品类,有效降低了客户端与具体产品类之间的耦合性。
- 产品族之间的一致性:抽象工厂模式保证创建的产品对象属于同一产品族,它们之间是相互匹配的,因此能够保证创建的产品对象之间能够正常协作。
- 灵活性高:通过切换具体工厂,可以在运行时创建不同的产品族,使系统具有较高的灵活性和可扩展性。
(2)缺点:
- 可扩展性受限:增加新的产品等级结构(新的抽象产品)较为困难,需要修改抽象工厂接口及其所有具体工厂的代码,违背了开闭原则。
- 复杂性增加:随着产品族和产品等级结构的增多,抽象工厂和具体工厂的数量会增加,导致系统的复杂度增加。
- 不易于单独新增产品:想要添加单独的产品类需要修改抽象工厂接口及其所有具体工厂的代码。
5.使用场景
- 当需要创建的对象是一系列相互关联或相互依赖的产品族时,如电器工厂中的电视机、洗衣机、空调等。
- 系统中有多个产品族,但每次只使用其中的某一族产品。如有人只喜欢穿某一个品牌的衣服和鞋。
- 系统中提供了产品的类库,且所有产品的接口相同,客户端不依赖产品实例的创建细节和内部结构。
6.模式扩展
(1)可以通过工厂模式 + 配置文件的方式解除工厂对象和产品对象的耦合。在工厂类中加载配置文件中的全
类名,并创建对象进行存储,客户端如果需要对象,直接进行获取即可。
(2)具体步骤如下:
- 定义配置文件 bean.properties
american=com.itheima.patterns.factory.config_factory.AmericanCoffee
latte=com.itheima.patterns.factory.config_factory.LatteCoffee
- 改进工厂类
package com.itheima.patterns.factory.config_factory;import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Properties;
import java.util.Set;public class CoffeeFactory {//加载配置文件,获取配置文件中配置的全类名,并创建该类的对象进行存储//1.定义容器对象来存储咖啡对象private static HashMap<String,Coffee> map = new HashMap<String, Coffee>();//2.加载配置文件,只需要加载一次static {//2.1.创建Properties对象Properties properties = new Properties();//2.2.调用properties对象中的load方法来加载配置文件InputStream is = CoffeeFactory.class.getClassLoader().getResourceAsStream("bean.properties");try {properties.load(is);//从properties集合中获取全类名并创建对象Set<Object> keys = properties.keySet();for (Object key : keys) {String className = properties.getProperty((String) key);//通过反射技术创建对象Class clazz = Class.forName(className);Coffee coffee = (Coffee) clazz.newInstance();//将名称和对象存储到容器中map.put((String) key, coffee);}} catch (Exception e) {e.printStackTrace();}}//根据名称获取对象public static Coffee createCoffee(String name) {return map.get(name);}
}
- 测试
package com.itheima.patterns.factory.config_factory;public class Client {public static void main(String[] args) {Coffee coffee1 = CoffeeFactory.createCoffee("american");System.out.println(coffee1.getName()); //美式咖啡Coffee coffee2 = CoffeeFactory.createCoffee("latte");System.out.println(coffee2.getName()); //拿铁咖啡}
}
静态成员变量用来存储创建的对象(键存储的是名称,值存储的是对应的对象),而读取配置文件以及创建对象写在静态代码块中,目的就是只需要执行一次。
有关 Properties 类的具体知识可以参考 Java 基础——Properties 类这篇文章。
7.JDK源码解析——Collection.iterator方法
(1)首先来看一下这一段代码:
package com.itheima.patterns.factory.iteratordemo;import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;public class IteratorDemo {public static void main(String[] args) {List<String> list = new ArrayList<>();list.add("Tom");list.add("Mike");list.add("Marry");//获取迭代器对象Iterator<String> iterator = list.iterator();//使用迭代器遍历while(iterator.hasNext()){String element = iterator.next();System.out.println(element);}}
}
大家应该很熟悉上面的代码,它使用迭代器遍历集合,获取集合中的元素。而单列集合获取迭代器的方法就使用到了工厂方法模式。现在通过类图来看其中的结构:
Collection 接口是抽象工厂类,ArrayList 是具体的工厂类;Iterator 接口是抽象商品类,ArrayList 类中的 Iter 内部类是具体的商品类。在具体的工厂类中 iterator() 方法创建具体的商品类的对象。
- Iterator.java
- Collection.java
- ArrayList.java
注:DateForamt 类中的 getInstance() 方法、Calendar 类中的 getInstance() 方法使用的也都是工厂模式。
相关文章:

Java 设计模式——抽象工厂模式
目录 1.概念2.结构3.实现4.优缺点5.使用场景6.模式扩展7.JDK源码解析——Collection.iterator方法 1.概念 (1)Java 设计模式——工厂方法模式中考虑的是一类产品的生产,如畜牧场只养动物、电视机厂只生产电视机等。这些工厂只生产同种类产品…...

如何使用ChatGPT构建一个Web应用程序?
围绕ChatGPT的最大卖点之一是它可以成为一种有效的编程工具。其想法是这样的:你用自然语言描述需求,该聊天机器人生成满足该需求的代码。但是ChatGPT在这方面到底有多好呢? 还有什么比亲自测试一下更好的方法呢?我们让ChatGPT从头…...

关闭手机广告的步骤
关闭手机广告的步骤 小米 1.设置→小米账号→声明与条款→系统广告→系统工具广告→关闭 2.设置→应用设置→应用管理→右上角三个点→设置→关闭“应用升级提醒”&“资源推荐” 3.桌面左滑打开负一屏→划到底部→设置→服务管理→选择关闭项目 4.桌面→打开任意文件夹…...

【Verilog 教程】6.6Verilog 仿真激励
关键词:testbench,仿真,文件读写 Verilog 代码设计完成后,还需要进行重要的步骤,即逻辑功能仿真。仿真激励文件称之为 testbench,放在各设计模块的顶层,以便对模块进行系统性的例化调用进行仿真…...

Win/Mac版Scitools Understand教育版申请
这里写目录标题 前言教育版申请流程教育账号申请 前言 上篇文章为大家介绍了Scitools Understand软件,通过领取的反馈来看有很多朋友都想用这个软件,但是我的网盘里只存了windows的pojie版,没有mac版的,我没有去网上找相关的资源…...

第十四届蓝桥杯大赛软件赛决赛 C/C++ 大学 B 组 试题 C: 班级活动
[蓝桥杯 2023 国 B] 班级活动 【问题描述】 小明的老师准备组织一次班级活动。班上一共有 n n n 名( n n n 为偶数)同学,老师想把所有的同学进行分组,每两名同学一组。为了公平,老师给每名同学随机分配了一个 n n …...

YOLOv8改进新颖的Gather-and-Distribute机制,低阶高阶新颖融合,增强了多尺度特征融合能力,实现了延迟和准确性的理想平衡
💡本篇内容:YOLOv8改进新颖的Gather-and-Distribute机制,低阶高阶新颖融合,增强了多尺度特征融合能力,实现了延迟和准确性的理想平衡 💡🚀🚀🚀本博客 改进源代码改进 适用于 YOLOv8 按步骤操作运行改进后的代码即可 💡本文提出改进 原创 方式:二次创新,YOL…...

面试算法13:二维子矩阵的数字之和
题目 输入一个二维矩阵,如何计算给定左上角坐标和右下角坐标的子矩阵的数字之和?对于同一个二维矩阵,计算子矩阵的数字之和的函数可能由于输入不同的坐标而被反复调用多次。例如,输入图2.1中的二维矩阵,以及左上角坐标…...

Vue安装插件时候中遇到冲突依赖解决方案
错误如下: npm ERR! code ERESOLVE npm ERR! ERESOLVE could not resolve npm ERR! npm ERR! While resolving: vue/eslint-config-standard6.1.0 npm ERR! Found: eslint-plugin-vue8.7.1 npm ERR! node_modules/eslint-plugin-vue npm ERR! dev eslint-pl…...

realloc函数应用IO泄露体验
本题主要介绍realloc函数,平时我们使用realloc最多便是在打malloc_hook–>onegadget的时候,使用realloc_hook调整onegadget的栈帧,从而getshell。 在realloc函数中,也能像malloc一样创建堆,并且比malloc麻烦一些&a…...

(c语言)野指针
#include<stdio.h> //野指针 int* test() { int a 10; return &a; } int main() { //野指针一: int* p; *p 10; //非法访问内存 //p没有初始化,就意味着没有明确的指向 //一个局部变量不初始化的话ÿ…...

【Git】轻松学会 Git(一):掌握 Git 的基本操作
文章目录 前言一、创建 Git 本地仓库1.1 什么是仓库1.2 创建本地仓库1.3 .git 目录结构 二、配置 Git三、认识 Git 的工作区、暂存区和版本库3.1 什么是 Git 的工作区、暂存区和版本库3.2 工作区、暂存区和版本库之间的关系 四、添加文件4.1 添加文件到暂存区和版本库中的命令4…...

rust trait对象
在拥有继承的语言中,可以定义一个名为shape的基类,该类上有一个draw方法。其他的类比如Button、SelectBox继承shape。它们各自覆盖draw方法。调用这些子类的draw方法时,就可以把它们统一当作shape来使用。不过Rust并没有继承,如果…...

Linux学习第21天:Linux内核定时器驱动开发: 流淌的时间长河
Linux版本号4.1.15 芯片I.MX6ULL 大叔学Linux 品人间百味 思文短情长 在人类的发展进化中,时间是一个非常重要神秘的物质量。任何事物都是在时间的长河中流淌发生、发展、变化。我们进行驱动开发中对时间的定义和使用也是…...

Centos服务在服务器重启后自启
以Dolphin为例 打开rc.local文件以编辑: sudo vi /etc/rc.d/rc.local在文件中添加您的启动命令。在您的情况下,要添加的命令如下: sh /opt/dolphinscheduler/zookeeper/bin/zkServer.sh start sh /opt/dolphinscheduler/dolphinscheduler/…...

慢性疼痛治疗服务公司Kindly MD申请700万美元纳斯达克IPO上市
来源:猛兽财经 作者:猛兽财经 猛兽财经获悉,慢性疼痛治疗服务公司Kindly MD近期已向美国证券交易委员会(SEC)提交招股书,申请在纳斯达克IPO上市,股票代码为(KDLY),Kindly MD计划通过…...

代码随想录 Day6 哈希 LeetcodeT454 四数之和II T383赎金信 T15 三数之和 T18 四数之和
本文代码思路来源于: 代码随想录 前言 希望大家在刷这部分题的时候先熟悉熟悉哈希结构的基本常用api,比较方便理解. LeetCode T454 四数之和 题目链接:454. 四数相加 II - 力扣(LeetCode) 题目思路 暴力解法仍然是遍历四个数组解决此题, 哈希的思路有…...

干货速来|教你如何撰写毕业论文
撰写毕业论文对于正常大学毕业至关重要。毕业论文是对学生在大学期间所学知识的综合运用和深入研究的体现,也是对学术能力和独立思考能力的考验。 撰写毕业论文的过程需要学生投入大量的时间和精力,包括选题、文献综述、研究方法选择、数据收集和分析、…...

【ROS 2】-2 话题通信
飞书原文链接: Docs...

Unity之NetCode多人网络游戏联机对战教程(2)--简单实现联机
文章目录 1.添加基本组件2.创建NetworkManager组件3.创建Player4.创建地面5.创建GameManager6.编译运行7. 测试联机后话 1.添加基本组件 NetworkManagerPlayerScene 2.创建NetworkManager组件 创建一个空物体,命名为NetworkManager 选择刚刚创建的NetworkManager…...
makdown文法
这里写自定义目录标题 欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants 创建一个自定义列表如何创建一个…...

新手程序员怎么接单?
程序员如何在自己年富力强的时候,最大化发挥自己的能力?将超能力转化为“钞能力”? 有人还在苦哈哈当老黄牛,一身使不完的牛劲,有人已经另辟蹊径,开创了自己的一片致富小天地。 接单找兼职,就…...

接口测试——接口协议抓包分析与mock_L2
目录: 抓包工具charles抓包工具fiddler抓包工具证书配置app抓包实战练习接口测试实战练习 1.抓包工具charles 工具介绍 支持 SSL 代理支持流量控制支持重发网络请求,方便后端调试支持修改网络请求参数支持网络请求的截获并动态修改可以自动将 json 或…...

Seata入门系列【1】安装seata 1.7.1+nacos 2.1.1
1 介绍 Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。 Github: https://github.com/seata/seata 官方文档:h…...

2023年职业院校技能大赛中职组----大数据应用与服务赛项任务书试题
2023年职业院校技能大赛中职组----大数据应用与服务赛项任务书试题 模块一:数据库系统运维(25分)任务一:数据库系统搭建(10分)任务二:房源数据库系统运维(15分) 模块二&a…...

产品经理的职业前景怎么样?一文为你全面解答!
随着科技的迅速发展和市场竞争的日益激烈,产品经理这个职业变得越来越炙手可热。产品经理负责一款产品的全生命周期管理,从需求收集到设计、开发、测试、发布,再到市场推广和用户反馈,都需要产品经理参与决策。因此,这…...

【深度学习】图像去噪(2)——常见网络学习
【深度学习】图像去噪 是在 【深度学习】计算机视觉 系列文章的基础上,再次针对深度学习(尤其是图像去噪方面)的基础知识有更深入学习和巩固。 1 DnCNN 1.1 网络结构 1.1.1 残差学习 1.1.2 Batch Normalization (BN) 1.1.2.1 背景和目标…...

八大排序详解
目录 1.排序的概念及应用 1.1 排序的概念 1.2 排序的应用 1.3 常见的排序算法 2.常见排序算法的实现 2.1 直接插入排序 2.1.1 基本思想 2.1.2 动图解析 2.1.3 排序步骤(默认升序) 2.1.4 代码实现 2.1.5 特性总结 2.2 希尔排序 2.2.1 基本思…...

自定义热加载:如何不停机实现核心代码更新
文章目录 1. 常见的几种实现代码热更新的几种方式对于开发环境我们可以使用部署环境1. 使用 Arthas 的 redefine 命令来加载新的 class 文件2. 利用 URLClassLoader 动态加载3. 通过Java的Instrumentation API 也是可以实现的 2. 实现1. ClassScanner扫描目录和加载类2. 定时任…...

Spring Cloud Alibaba Nacos 2.2.3 (2) - 单机版启动 (winodows 和 linux )
Nacos 2.2.3 (1) - 下载与数据库配置 参考下载与数据库配置 启动服务器 执行 nacos-server-2.2.3\bin 下的startup.sh或者startup.cmd (根据不同系统) windows 下nacos 单机启动 方式一: 1,打开cmd 2,cd 到nacos-s…...