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

label-studio的使用教程(导入本地路径)
文章目录 1. 准备环境2. 脚本启动2.1 Windows2.2 Linux 3. 安装label-studio机器学习后端3.1 pip安装(推荐)3.2 GitHub仓库安装 4. 后端配置4.1 yolo环境4.2 引入后端模型4.3 修改脚本4.4 启动后端 5. 标注工程5.1 创建工程5.2 配置图片路径5.3 配置工程类型标签5.4 配置模型5.…...

从WWDC看苹果产品发展的规律
WWDC 是苹果公司一年一度面向全球开发者的盛会,其主题演讲展现了苹果在产品设计、技术路线、用户体验和生态系统构建上的核心理念与演进脉络。我们借助 ChatGPT Deep Research 工具,对过去十年 WWDC 主题演讲内容进行了系统化分析,形成了这份…...
逻辑回归:给不确定性划界的分类大师
想象你是一名医生。面对患者的检查报告(肿瘤大小、血液指标),你需要做出一个**决定性判断**:恶性还是良性?这种“非黑即白”的抉择,正是**逻辑回归(Logistic Regression)** 的战场&a…...

LeetCode - 394. 字符串解码
题目 394. 字符串解码 - 力扣(LeetCode) 思路 使用两个栈:一个存储重复次数,一个存储字符串 遍历输入字符串: 数字处理:遇到数字时,累积计算重复次数左括号处理:保存当前状态&a…...
基础测试工具使用经验
背景 vtune,perf, nsight system等基础测试工具,都是用过的,但是没有记录,都逐渐忘了。所以写这篇博客总结记录一下,只要以后发现新的用法,就记得来编辑补充一下 perf 比较基础的用法: 先改这…...

MySQL 8.0 OCP 英文题库解析(十三)
Oracle 为庆祝 MySQL 30 周年,截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始,将英文题库免费公布出来,并进行解析,帮助大家在一个月之内轻松通过OCP认证。 本期公布试题111~120 试题1…...
高防服务器能够抵御哪些网络攻击呢?
高防服务器作为一种有着高度防御能力的服务器,可以帮助网站应对分布式拒绝服务攻击,有效识别和清理一些恶意的网络流量,为用户提供安全且稳定的网络环境,那么,高防服务器一般都可以抵御哪些网络攻击呢?下面…...
鸿蒙DevEco Studio HarmonyOS 5跑酷小游戏实现指南
1. 项目概述 本跑酷小游戏基于鸿蒙HarmonyOS 5开发,使用DevEco Studio作为开发工具,采用Java语言实现,包含角色控制、障碍物生成和分数计算系统。 2. 项目结构 /src/main/java/com/example/runner/├── MainAbilitySlice.java // 主界…...
Python ROS2【机器人中间件框架】 简介
销量过万TEEIS德国护膝夏天用薄款 优惠券冠生园 百花蜂蜜428g 挤压瓶纯蜂蜜巨奇严选 鞋子除臭剂360ml 多芬身体磨砂膏280g健70%-75%酒精消毒棉片湿巾1418cm 80片/袋3袋大包清洁食品用消毒 优惠券AIMORNY52朵红玫瑰永生香皂花同城配送非鲜花七夕情人节生日礼物送女友 热卖妙洁棉…...
#Uniapp篇:chrome调试unapp适配
chrome调试设备----使用Android模拟机开发调试移动端页面 Chrome://inspect/#devices MuMu模拟器Edge浏览器:Android原生APP嵌入的H5页面元素定位 chrome://inspect/#devices uniapp单位适配 根路径下 postcss.config.js 需要装这些插件 “postcss”: “^8.5.…...