当前位置: 首页 > 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 附带以…...

设计模式 — — 代理模式

一、是什么 代理模式&#xff08;Proxy Pattern&#xff09;是为一个对象提供一个代用品或占位符&#xff0c;以便控制对它的 生活场景&#xff1a; 租房、买房&#xff0c;比如链家等房屋中介机构&#xff0c;起到的作用就是代理 二、使用 const proxy new Proxy(target, …...

【高通camera hal bug分析】高通自带相机镜像问题

首先打了两个log&#xff0c;一个是开启镜像的log&#xff0c;还有一个是没有开启镜像的log&#xff0c;如果我们开启镜像以后&#xff0c;观察开启镜像log发现 , 这段代码走的没有任何问题&#xff0c;因为Flip的值等于1了。 关闭镜像log如下&#xff1a; 如果我们不开启镜像…...

EPICS和Arduino Uno之间基于串行文本协议的控制开发

Arduino Uno的串口服务程序设置如文本的串口通信协议设计以及在Arduino上的应用-CSDN博客中所示。通过在串口上发送约定的文本协议&#xff0c;它实现的功能如下&#xff1a; 实现功能&#xff1a; 读取三路0.0V~5.0V模拟量输入&#xff0c;读取端口A0~A2设置三路0.0V~5.0V的模…...

数据结构的概念大合集02(线性表)

概念大合集02 1、线性表及其逻辑结构1.1 线性表的定义1.2 线性表的基本操作 2、线性表的顺序存储结构2.1 顺序表 3、线性表的链式存储3.1 链表3.1.1 头结点&#xff08;头指针&#xff09;&#xff0c;首指针&#xff0c;尾指针&#xff0c;尾结点3.1.2 单链表3.1.3 双链表3.1.…...

CSS3DRenderer, CSS3DSprite API 使用案例demo

CSS3DRenderer, CSS3DSprite API 使用案例demo <!DOCTYPE html> <html><head><title>three.js css3d - sprites</title><meta charset"utf-8"><meta name"viewport" content"widthdevice-width, user-scalabl…...

河马优化算法(HO)-2024年Nature子刊新算法 公式原理详解与性能测评 Matlab代码免费获取

声明&#xff1a;文章是从本人公众号中复制而来&#xff0c;因此&#xff0c;想最新最快了解各类智能优化算法及其改进的朋友&#xff0c;可关注我的公众号&#xff1a;强盛机器学习&#xff0c;不定期会有很多免费代码分享~ 目录 原理简介 一、种群初始化 二、河马在河流或…...

SLAM 算法综述

LiDAR SLAM 其主要思想是通过两个算法&#xff1a;一个高频激光里程计进行低精度的运动估计&#xff0c;即使用激光雷达做里程计计算两次扫描之间的位姿变换&#xff1b;另一个是执行低频但是高精度的建图与校正里程计&#xff0c;利用多次扫描的结果构建地图&#xff0c;细化位…...

搭建Hadoop3.x完全分布式集群

零、资源准备 虚拟机相关&#xff1a; VMware workstation 16&#xff1a;虚拟机 > vmware_177981.zipCentOS Stream 9&#xff1a;虚拟机 > CentOS-Stream-9-latest-x86_64-dvd1.iso Hadoop相关 jdk1.8&#xff1a;JDK > jdk-8u261-linux-x64.tar.gzHadoop 3.3.6&am…...

linux常用命令(二)

目录 前言 常用命令 1.ls命令 2. cd命令 3.pwd命令 4.mkdir 命令 5. rmdir 命令 6.rm 命令 7.cp命令 8.mv命令 9.touch命令 10.cat命令 11.more命令 12.less命令 13.head命令 14.tail命令 15.tail命令 16.find命令 17.tar命令 18.gzip命令 19.gunzip命令 …...

【Vue】Request模块 - axios 封装Vuex的持久化存储

&#x1f4dd;个人主页&#xff1a;五敷有你 &#x1f525;系列专栏&#xff1a;Vue ⛺️稳中求进&#xff0c;晒太阳 Request模块 - axios 封装 使用axios来请求后端接口&#xff0c;一般会对axios进行一些配置&#xff08;比如配置基础地址&#xff0c;请求响应拦截器…...