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…...

铭豹扩展坞 USB转网口 突然无法识别解决方法
当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...

Spark 之 入门讲解详细版(1)
1、简介 1.1 Spark简介 Spark是加州大学伯克利分校AMP实验室(Algorithms, Machines, and People Lab)开发通用内存并行计算框架。Spark在2013年6月进入Apache成为孵化项目,8个月后成为Apache顶级项目,速度之快足见过人之处&…...
深入浅出:JavaScript 中的 `window.crypto.getRandomValues()` 方法
深入浅出:JavaScript 中的 window.crypto.getRandomValues() 方法 在现代 Web 开发中,随机数的生成看似简单,却隐藏着许多玄机。无论是生成密码、加密密钥,还是创建安全令牌,随机数的质量直接关系到系统的安全性。Jav…...

selenium学习实战【Python爬虫】
selenium学习实战【Python爬虫】 文章目录 selenium学习实战【Python爬虫】一、声明二、学习目标三、安装依赖3.1 安装selenium库3.2 安装浏览器驱动3.2.1 查看Edge版本3.2.2 驱动安装 四、代码讲解4.1 配置浏览器4.2 加载更多4.3 寻找内容4.4 完整代码 五、报告文件爬取5.1 提…...

关键领域软件测试的突围之路:如何破解安全与效率的平衡难题
在数字化浪潮席卷全球的今天,软件系统已成为国家关键领域的核心战斗力。不同于普通商业软件,这些承载着国家安全使命的软件系统面临着前所未有的质量挑战——如何在确保绝对安全的前提下,实现高效测试与快速迭代?这一命题正考验着…...
Angular微前端架构:Module Federation + ngx-build-plus (Webpack)
以下是一个完整的 Angular 微前端示例,其中使用的是 Module Federation 和 npx-build-plus 实现了主应用(Shell)与子应用(Remote)的集成。 🛠️ 项目结构 angular-mf/ ├── shell-app/ # 主应用&…...

搭建DNS域名解析服务器(正向解析资源文件)
正向解析资源文件 1)准备工作 服务端及客户端都关闭安全软件 [rootlocalhost ~]# systemctl stop firewalld [rootlocalhost ~]# setenforce 0 2)服务端安装软件:bind 1.配置yum源 [rootlocalhost ~]# cat /etc/yum.repos.d/base.repo [Base…...
MySQL 部分重点知识篇
一、数据库对象 1. 主键 定义 :主键是用于唯一标识表中每一行记录的字段或字段组合。它具有唯一性和非空性特点。 作用 :确保数据的完整性,便于数据的查询和管理。 示例 :在学生信息表中,学号可以作为主键ÿ…...

Scrapy-Redis分布式爬虫架构的可扩展性与容错性增强:基于微服务与容器化的解决方案
在大数据时代,海量数据的采集与处理成为企业和研究机构获取信息的关键环节。Scrapy-Redis作为一种经典的分布式爬虫架构,在处理大规模数据抓取任务时展现出强大的能力。然而,随着业务规模的不断扩大和数据抓取需求的日益复杂,传统…...

破解路内监管盲区:免布线低位视频桩重塑停车管理新标准
城市路内停车管理常因行道树遮挡、高位设备盲区等问题,导致车牌识别率低、逃费率高,传统模式在复杂路段束手无策。免布线低位视频桩凭借超低视角部署与智能算法,正成为破局关键。该设备安装于车位侧方0.5-0.7米高度,直接规避树枝遮…...