当前位置: 首页 > news >正文

【设计模式】Java 设计模式之工厂模式(Factory Pattern)

工厂模式(Factory Pattern)深入解析

一、工厂模式概述

工厂模式是一种创建型设计模式,它提供了一种封装对象创建过程的方式,将对象的创建与使用分离。工厂模式的核心思想是将“实例化对象”的操作与“使用对象”的操作分开,将实例化对象的责任交给专门的工厂类负责,这样可以降低系统的耦合度,提高系统的可扩展性和可维护性。

二、工厂模式结构

工厂模式主要包括三个角色:

  1. 抽象产品(Product)角色:定义了产品的接口,工厂方法所创建的对象的超类型,即产品对象的共同接口。
  2. 具体产品(Concrete Product)角色:实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间是一对多关系。
  3. 工厂(Factory)角色:负责实现创建产品对象的实例。

三、工厂模式的实现方式

工厂模式主要分为三种:简单工厂模式、工厂方法模式和抽象工厂模式。

  1. 简单工厂模式:通过一个具体的工厂类来创建具体的产品对象,所有的产品对象都来自同一个工厂。

示例代码:

// 抽象产品
interface Car {void drive();
}// 具体产品
class BMW implements Car {@Overridepublic void drive() {System.out.println("Driving BMW");}
}class Benz implements Car {@Overridepublic void drive() {System.out.println("Driving Benz");}
}// 工厂类
class CarFactory {public static Car createCar(String type) {if ("BMW".equalsIgnoreCase(type)) {return new BMW();} else if ("Benz".equalsIgnoreCase(type)) {return new Benz();}return null;}
}// 客户端代码
public class Client {public static void main(String[] args) {Car car1 = CarFactory.createCar("BMW");car1.drive();Car car2 = CarFactory.createCar("Benz");car2.drive();}
}
  1. 工厂方法模式:定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。

示例代码:

// 抽象产品
interface Car {void drive();
}// 具体产品
class BMW implements Car {@Overridepublic void drive() {System.out.println("Driving BMW");}
}class Benz implements Car {@Overridepublic void drive() {System.out.println("Driving Benz");}
}// 抽象工厂
interface CarFactory {Car createCar();
}// 具体工厂
class BMWFactory implements CarFactory {@Overridepublic Car createCar() {return new BMW();}
}class BenzFactory implements CarFactory {@Overridepublic Car createCar() {return new Benz();}
}// 客户端代码
public class Client {public static void main(String[] args) {CarFactory bmwFactory = new BMWFactory();Car bmw = bmwFactory.createCar();bmw.drive();CarFactory benzFactory = new BenzFactory();Car benz = benzFactory.createCar();benz.drive();}
}
  1. 抽象工厂模式:提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。

由于抽象工厂模式较为复杂,这里不展开代码示例。

四、工厂模式的优缺点

优点

  1. 封装性好:客户端不需要知道具体产品类的类名,只需要知道所对应的产品工厂即可。
  2. 解耦:将产品的创建与使用分离,降低系统的耦合度。
  3. 扩展性好:当需要增加新的产品时,只需要增加新的具体产品类和对应的具体工厂类,原有系统不需要做修改。

缺点

  1. 增加系统复杂性:由于增加了工厂类,系统的抽象性和复杂性也随之增加。
  2. 不利于产品族中产品的扩展:一个产品族中的多个对象被一起使用时,不易单独改变某一个产品的实现。

五、工厂模式的应用场景

  1. 当一个类不知道它所必须创建的对象的类的时候。
  2. 当一个类希望由它的子类来指定它所创建的对象的时候。
  3. 当类将创建对象的职责委托给多个帮助子类的某一个,并且你希望将哪一个帮助子类是代理者这一信息局部化的时候。

例如,在软件系统中,经常需要创建一些不同类型的对象,而这些对象的创建过程可能比较复杂,或者需要依赖于一些配置信息。在这种情况下,可以使用工厂模式来简化对象的创建过程,并提高系统的可维护性和可扩展性。

六、实际案例解读

以日志记录为例,不同的系统可能需要使用不同的日志库,如Log4j、SLF4J等。使用工厂模式,我们可以根据配置文件或者运行时参数,动态地创建并使用不同的日志对象。

首先,定义日志接口和具体的日志实现类:

// 日志接口
interface Logger {void log(String message);
}// Log4j实现
class Log4jLogger implements Logger {@Overridepublic void log(String message) {System.out.println("Log4j: " + message);}
}// SLF4J实现
class SLF4JLogger implements Logger {@Overridepublic void log(String message) {System.out.println("SLF4J: " + message);}
}

然后,创建日志工厂类:

// 日志工厂
class LoggerFactory {public static Logger createLogger(String type) {if ("Log4j".equalsIgnoreCase(type)) {return new Log4jLogger();} else if ("SLF4J".equalsIgnoreCase(type)) {return new SLF4JLogger();}throw new IllegalArgumentException("Invalid logger type: " + type);}
}

最后,在客户端代码中,通过工厂类创建并使用日志对象:

public class Client {public static void main(String[] args) {// 根据配置或参数选择日志类型String loggerType = "Log4j"; // 可以从配置文件或环境变量中获取Logger logger = LoggerFactory.createLogger(loggerType);// 使用日志对象logger.log("This is a log message.");}
}

在这个例子中,客户端代码只需要通过LoggerFactorycreateLogger方法来获取一个日志对象,而不需要关心具体的日志实现类。这样,如果需要更换日志库,只需要修改工厂类的实现,而不需要修改客户端代码,从而提高了系统的可维护性和可扩展性。
七、工厂模式的变体和注意事项

除了上述的基本工厂模式,还存在一些变体,例如多重工厂模式、静态工厂模式等。多重工厂模式用于创建多个不同类型的产品族,而静态工厂模式则通过静态方法来创建对象,避免了实例化工厂类的开销。

在使用工厂模式时,需要注意以下几点:

  1. 设计得当的抽象层:确保产品接口和工厂接口设计得当,能够充分表达所需的功能和约束。
  2. 避免过度使用:工厂模式虽然能够降低耦合度,但过度使用可能导致系统变得复杂和难以理解。应根据实际需要来决定是否使用工厂模式。
  3. 配置管理:对于需要根据配置或运行时参数动态创建对象的场景,需要妥善管理这些配置信息,确保它们能够正确地指导工厂创建对象。
  4. 单例工厂:在某些情况下,工厂本身可能只需要一个实例,这时可以考虑使用单例模式来确保工厂的唯一性。

八、工厂模式的进阶使用

在软件开发过程中,工厂模式不仅可以单独使用,还可以与其他设计模式结合,形成更强大的解决方案。下面列举几个工厂模式与其他设计模式结合的示例:

  1. 工厂模式与原型模式:当创建对象的成本较高,或者需要频繁创建具有相同属性的对象时,可以结合使用原型模式。原型模式通过复制现有对象来创建新对象,而工厂模式负责管理这些原型的创建和复制过程。

  2. 工厂模式与单例模式:在某些情况下,工厂本身只需要一个实例,这时可以结合使用单例模式。单例模式确保一个类只有一个实例,并提供一个全局访问点。通过将工厂设计为单例,可以确保系统中只有一个工厂实例,从而避免创建多个工厂对象带来的开销和混乱。

  3. 工厂模式与依赖注入:依赖注入是一种将依赖关系从代码中解耦的技术,它允许在运行时动态地将依赖关系注入到对象中。工厂模式可以与依赖注入结合使用,由工厂负责创建对象并注入所需的依赖关系。这样可以使代码更加灵活和可测试,并降低类之间的耦合度。

九、工厂模式在现代框架和库中的应用

工厂模式在许多现代编程框架和库中都有广泛应用。这些框架和库通过内置工厂模式,为开发者提供了便捷的对象创建和管理机制。例如,Spring框架中的BeanFactory就是工厂模式的一个应用实例,它负责根据配置文件或注解动态地创建和管理bean对象。类似的,在GUI框架中,也经常使用工厂模式来创建和管理窗口、按钮等界面元素。

十、工厂模式的挑战与限制

虽然工厂模式具有许多优点,但也存在一些挑战和限制。首先,过度使用工厂模式可能导致系统变得复杂和难以理解。每个工厂类都需要维护一套创建逻辑,如果工厂数量过多或创建逻辑过于复杂,就会增加系统的维护成本。其次,工厂模式可能隐藏了具体的实现细节,使得调试和排查问题变得更加困难。此外,工厂模式也可能引入额外的性能开销,特别是在创建大量对象时。

十一、结论

工厂模式是一种强大而灵活的设计模式,它可以帮助我们封装对象的创建过程,降低系统的耦合度,提高可扩展性和可维护性。然而,在使用工厂模式时,我们需要谨慎评估其适用性,避免过度使用带来的问题。同时,我们也应该结合其他设计模式和技术手段,形成更完善的解决方案。通过不断学习和实践,我们可以更好地利用工厂模式来构建高质量的软件系统。

相关文章:

【设计模式】Java 设计模式之工厂模式(Factory Pattern)

工厂模式(Factory Pattern)深入解析 一、工厂模式概述 工厂模式是一种创建型设计模式,它提供了一种封装对象创建过程的方式,将对象的创建与使用分离。工厂模式的核心思想是将“实例化对象”的操作与“使用对象”的操作分开&…...

安卓UI面试题 36-40

36. 简述 getDimension、getDimensionPixelOffset 和 getDimensionPixelSize 三者的区别? 相同点 单位为dp/sp时,都会乘以density,单位为px则不乘不同点 1、getDimension返回的是float值 2、getDimensionPixelSize,返回的是int值,float转成int时,四舍五入 3、getDimensio…...

Java有哪些常用的集合?

1、典型回答 在 Java 中,常用的集合有以下几个: 列表(List):有序集合,可以包含重复元素。常见实现类有 ArrayList、LinkedList、 Vector 等集合(Set):无序集合,不允许包含重复元素。常见实现类有 HashSet、…...

虚拟机网络链接

在虚拟网络设置中找到如下界面: "子网 IP" 192.168.79.0/24 表示一个局域网络,它有254个可能的IP地址可供分配(192.168.79.1到192.168.79.254),255.255.255.0 是子网掩码,定义了网络和主机部分。…...

代码随想录阅读笔记-字符串【反转字符串】

题目 编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 char[] 的形式给出。 不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。 你可以假设数组中的所有字符都是 ASCII 码表中的可打印…...

4. Linux文件属性和目录系列

在 Linux 系统中,文件和目录是基本的文件系统组成部分。文件系统是用于组织和存储文件的一种结构,而文件和目录则是文件系统的核心元素。以下是对 Linux 文件和目录的详细解释: 1. 文件(File) 在 Linux 中,文件是数据的集合,可以是文本文件、二进制文件、设备文件等。…...

Linux第78步_使用原子整型操作来实现“互斥访问”共享资源

使用原子操作来实现“互斥访问”LED灯设备,目的是每次只允许一个应用程序使用LED灯。 1、创建MyAtomicLED目录 输入“cd /home/zgq/linux/Linux_Drivers/回车” 切换到“/home/zgq/linux/Linux_Drivers/”目录 输入“mkdir MyAtomicLED回车”,创建MyA…...

C++——C++11(3)

C——C11(3) lambda表达式(匿名的仿函数对象)一些注意点lambda捕捉列表[][&][this] lambda的赋值 function包装器function成员函数的包装 bind绑定参数 我们今天接着来了解一下C11一些新的特性,如果还没有看过上两…...

更改el-tabs默认样式,实现tab标签居中显示,标签对应内容使用另一个div显示

首先看效果图 如图所示&#xff0c;标签在浏览器窗口居中&#xff0c;但是下面的内容依然是默认从左到右&#xff0c;不会受到tab样式的影响 <template><div><div style"display: flex; justify-content: center; align-items: center;"><el-…...

微信小程序原生<map>地图实现标记多个位置以及map 组件 callout 自定义气泡

一、老规矩先上效果图: 二、在pages文件夹下新建image文件夹用来存放标记的图片。 三、代码片段 也可以参考小程序文档:https://developers.weixin.qq.com/miniprogram/dev/component/map.html index.wxml代码 <mapid="map"style="width: 100%; height:1…...

外包干了3天,技术明显进步。。。。。

先说一下自己的情况&#xff0c;本科生&#xff0c;19年通过校招进入南京某软件公司&#xff0c;干了接近2年的功能测试&#xff0c;今年年初&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落!而我已经在一个企业干了2年的功能测试&…...

Transformer学习笔记(二)

一、文本嵌入层Embedding 1、作用&#xff1a; 无论是源文本嵌入还是目标文本嵌入&#xff0c;都是为了将文本中词汇的数字表示转变为向量表示&#xff0c;希望在这样的高维空间捕捉词汇间的关系。 二、位置编码器Positional Encoding 1、作用&#xff1a; 因为在Transformer…...

C#求水仙花数

目录 1.何谓水仙花数 2.求三位数的水仙花数 3.在遍历中使用Math.DivRem方法再求水仙花数 1.何谓水仙花数 水仙花数&#xff08;Narcissistic number&#xff09;是指一个 n 位正整数&#xff0c;它的每个位上的数字的 n 次幂之和等于它本身。例如&#xff0c;153 是一个 3 …...

FFmpeg转码参数说明及视频转码示例

-b : 设置音频或者视频的转码码率 -b:v 只设置视频码率 -b:a 只设置音频码率 -ab: 只设置音频码率, 默认码率大小为: 128k bit/s -g: 设置视频GOP大小,表示I帧之间的间隔,默认为12 -ar: 设置音频采样率,默认0 -ac: 设置音频通道数量 默认0 -bf: 设置连…...

qiankun:vite/webpack项目配置

相关博文&#xff1a; https://juejin.cn/post/7216536069285429285?searchId202403091501088BACFF113F980BA3B5F3 https://www.bilibili.com/video/BV12T411q7dq/?spm_id_from333.337.search-card.all.click qiankun结构&#xff1a; 主应用base&#xff1a;vue3historyv…...

【Linux】深入了解Linux磁盘配额:限制用户磁盘空间的利器

&#x1f34e;个人博客&#xff1a;个人主页 &#x1f3c6;个人专栏&#xff1a;Linux ⛳️ 功不唐捐&#xff0c;玉汝于成 前言 在多用户环境下管理磁盘空间是服务器管理中的一项重要任务。Linux提供了强大的磁盘配额功能&#xff0c;可以帮助管理员限制用户或组对文件系统…...

Kamailio Debian安装

新方法是&#xff1a; apt install -y gnupg2 wget -O- https://deb.kamailio.org/kamailiodebkey.gpg | gpg --dearmor | tee /usr/share/keyrings/kamailio.gpg 老方法是&#xff1a; apt install -y gnupg2 wget -O- http://deb.kamailio.org/kamailiodebkey.gpg | apt-key…...

web学习笔记(三十四)

目录 1.面向对象的特征 2.面向对象的继承方式 3.正则表达式 3.1如何创建正则表达式 3.2边界符 3.2[ ]方括号 3.3正则表达式中相关的方法汇总 1.面向对象的特征 封装性&#xff1a;就像是把东西放在一个密封的盒子里一样&#xff0c;只让外部使用者通过指定的接口来访…...

2024/03/16----面试中遇到的一些面试题

1.请简单的说一下IOC&#xff0c;AOP 1.1 IOC 控制反转&#xff08;IOC&#xff09;是一种设计思想&#xff0c;就是将原本在程序中需要手动创建对象&#xff0c;现在交由Spring管理创建&#xff0c;从而降低代码之间的耦合度。 IoC 最常见以及最合理的实现方式叫做依赖注入…...

【SysBench】Linux 安装 sysbench-1.20

安装目的是为了对 MySQL 8.0.x 、PostgreSQL 进行基准测试。 0、sysbench 简介 sysbench 是一个可编写脚本的多线程基准测试工具&#xff0c;基于 LuaJIT 。 它最常用于数据库基准测试&#xff0c;但也可以 用于创建任意不涉及数据库服务器的复杂工作负载。 sysbench 附带以…...

Python|GIF 解析与构建(5):手搓截屏和帧率控制

目录 Python&#xff5c;GIF 解析与构建&#xff08;5&#xff09;&#xff1a;手搓截屏和帧率控制 一、引言 二、技术实现&#xff1a;手搓截屏模块 2.1 核心原理 2.2 代码解析&#xff1a;ScreenshotData类 2.2.1 截图函数&#xff1a;capture_screen 三、技术实现&…...

【根据当天日期输出明天的日期(需对闰年做判定)。】2022-5-15

缘由根据当天日期输出明天的日期(需对闰年做判定)。日期类型结构体如下&#xff1a; struct data{ int year; int month; int day;};-编程语言-CSDN问答 struct mdata{ int year; int month; int day; }mdata; int 天数(int year, int month) {switch (month){case 1: case 3:…...

Cursor实现用excel数据填充word模版的方法

cursor主页&#xff1a;https://www.cursor.com/ 任务目标&#xff1a;把excel格式的数据里的单元格&#xff0c;按照某一个固定模版填充到word中 文章目录 注意事项逐步生成程序1. 确定格式2. 调试程序 注意事项 直接给一个excel文件和最终呈现的word文件的示例&#xff0c;…...

Oracle查询表空间大小

1 查询数据库中所有的表空间以及表空间所占空间的大小 SELECTtablespace_name,sum( bytes ) / 1024 / 1024 FROMdba_data_files GROUP BYtablespace_name; 2 Oracle查询表空间大小及每个表所占空间的大小 SELECTtablespace_name,file_id,file_name,round( bytes / ( 1024 …...

系统设计 --- MongoDB亿级数据查询优化策略

系统设计 --- MongoDB亿级数据查询分表策略 背景Solution --- 分表 背景 使用audit log实现Audi Trail功能 Audit Trail范围: 六个月数据量: 每秒5-7条audi log&#xff0c;共计7千万 – 1亿条数据需要实现全文检索按照时间倒序因为license问题&#xff0c;不能使用ELK只能使用…...

基于Uniapp开发HarmonyOS 5.0旅游应用技术实践

一、技术选型背景 1.跨平台优势 Uniapp采用Vue.js框架&#xff0c;支持"一次开发&#xff0c;多端部署"&#xff0c;可同步生成HarmonyOS、iOS、Android等多平台应用。 2.鸿蒙特性融合 HarmonyOS 5.0的分布式能力与原子化服务&#xff0c;为旅游应用带来&#xf…...

什么?连接服务器也能可视化显示界面?:基于X11 Forwarding + CentOS + MobaXterm实战指南

文章目录 什么是X11?环境准备实战步骤1️⃣ 服务器端配置(CentOS)2️⃣ 客户端配置(MobaXterm)3️⃣ 验证X11 Forwarding4️⃣ 运行自定义GUI程序(Python示例)5️⃣ 成功效果![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/55aefaea8a9f477e86d065227851fe3d.pn…...

HashMap中的put方法执行流程(流程图)

1 put操作整体流程 HashMap 的 put 操作是其最核心的功能之一。在 JDK 1.8 及以后版本中&#xff0c;其主要逻辑封装在 putVal 这个内部方法中。整个过程大致如下&#xff1a; 初始判断与哈希计算&#xff1a; 首先&#xff0c;putVal 方法会检查当前的 table&#xff08;也就…...

【 java 虚拟机知识 第一篇 】

目录 1.内存模型 1.1.JVM内存模型的介绍 1.2.堆和栈的区别 1.3.栈的存储细节 1.4.堆的部分 1.5.程序计数器的作用 1.6.方法区的内容 1.7.字符串池 1.8.引用类型 1.9.内存泄漏与内存溢出 1.10.会出现内存溢出的结构 1.内存模型 1.1.JVM内存模型的介绍 内存模型主要分…...

Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement

Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement 1. LAB环境2. L2公告策略2.1 部署Death Star2.2 访问服务2.3 部署L2公告策略2.4 服务宣告 3. 可视化 ARP 流量3.1 部署新服务3.2 准备可视化3.3 再次请求 4. 自动IPAM4.1 IPAM Pool4.2 …...