设计模式之旅:工厂模式全方位解析
简介
设计模式中与工厂模式相关的主要有三种,它们分别是:
- 简单工厂模式(Simple Factory):这不是GoF(四人帮,设计模式的开创者)定义的标准模式,但被广泛认为是工厂模式的一种简化。它实际上是一个类(工厂类),用于创建其他类的实例。客户端不直接创建对象,而是通过工厂类进行创建,这样就隐藏了实例创建的细节。简单工厂模式实际上违背了开闭原则(对扩展开放,对修改关闭),因为每次增加新的产品类时,都需要修改工厂类。
- 工厂方法模式(Factory Method):这是一个真正的设计模式,是GoF所定义的。在工厂方法模式中,创建对象的任务被转移到了一个方法中,但这个方法不是由创建者类(也就是工厂类)直接调用的,而是在其子类中实现的,这样的设计允许系统在不修改现有客户端代码的情况下引入新的产品类。这个模式遵循了开闭原则,使得增加新的产品类时,只需添加新的具体工厂类即可。
- 抽象工厂模式(Abstract Factory):这是一种更为复杂和灵活的工厂模式。它提供了一个接口,用于创建一系列相关或相互依赖的对象,而无需指定它们具体的类。抽象工厂允许客户端使用抽象的接口来创建一组相关的产品,而不需要知道(或关心)产品的具体类型。这种模式非常适用于系统中产品类的家族很多,而且这些产品是以系列的方式提供的情况。
简单来说,简单工厂模式注重于创建单一产品,工厂方法模式允许类的扩展,通过子类来指定创建哪个对象,而抽象工厂模式则提供一个创建产品家族的接口,使得创建一系列相关或依赖对象的客户端不依赖于产品的具体类。每种模式都有其适用的场景和优缺点,选择哪种模式取决于你面临的问题和设计的需求。
一、简单工厂模式
简单工厂模式(Simple Factory Pattern)是一种创建型设计模式,用于创建对象而不必暴露创建逻辑给客户端,并且通过使用一个共同的接口来指向新创建的对象。这种模式是工厂方法模式的一个特殊实现,但并不是一个真正的设计模式,更多的是一种编程习惯。
目的
简单工厂模式的主要目的是为了实现对象的创建与使用的分离。通过一个单独的工厂类来决定哪种产品类应当被实例化,这样可以减少系统的耦合度,增加系统的灵活性。
如何实现
简单工厂模式通常由三个角色组成:
- 工厂类(Factory):一个负责实现创建所有实例的方法,根据不同的参数返回不同类的实例。
- 抽象产品类(Product):所有具体产品类的父类,负责描述所有实例所共有的公共接口。
- 具体产品类(ConcreteProduct):工厂类所创建的对象类,它们都继承自抽象产品类。
示例
假设我们有一个软件,需要根据不同的文件类型(如文本文件、图像文件)来创建不同的视图来显示内容。我们可以使用简单工厂模式来设计这个功能。
首先,定义一个抽象产品类:
public abstract class FileView {public abstract void display();
}
然后,创建具体产品类:
public class TextView extends FileView {@Overridepublic void display() {System.out.println("Displaying text file");}
}public class ImageView extends FileView {@Overridepublic void display() {System.out.println("Displaying image file");}
}
接下来,创建工厂类:
public class FileViewFactory {// 使用 getFileView 方法获取类型的对象public static FileView getFileView(String fileType) {if (fileType == null) {return null;}if (fileType.equalsIgnoreCase("TEXT")) {return new TextView();} else if (fileType.equalsIgnoreCase("IMAGE")) {return new ImageView();}return null;}
}
最后,客户端代码可以这样使用工厂类:
public class FactoryDemo {public static void main(String[] args) {FileView fileView = FileViewFactory.getFileView("TEXT");fileView.display();fileView = FileViewFactory.getFileView("IMAGE");fileView.display();}
}
优点
- 工厂类包含必要的逻辑判断,可以决定在什么时候创建哪一个产品的实例。客户端可以免除直接创建对象的责任,而仅仅"消费"产品。
- 增加新的产品类时,只需要扩展工厂类即可,符合开闭原则(对扩展开放,对修改封闭)。
缺点
- 工厂类的职责相对过重,增加新的产品时,需要修改工厂类的判断逻辑,违背了开闭原则。
- 不易于扩展过于复杂的产品结构。如果产品的创建逻辑过于复杂,可能会导致工厂类变得非常庞大,难以维护。
简单工厂模式是理解工厂方法模式和抽象工厂模式的基础,它通过专门的工厂类将创建实例的任务和客户端使用分离,从而实现了系统的解耦和灵活性的提高。
二、工厂方法模式
工厂方法模式(Factory Method Pattern)是一种创建型设计模式,它定义了一个创建对象的接口,但让实例化的工作由其子类去完成。这样的设计模式使得一个类在不改变其代码的情况下增加新类型的产品。它是实现软件框架的一个重要方法,允许用户扩展框架中的部分功能,而无需修改框架的代码。
目的与应用场景
工厂方法模式主要用于以下情况:
- 当一个类不知道它所必须创建的对象的类的时候。
- 当一个类希望由它的子类来指定创建对象的时候。
- 当类将创建对象的职责委托给多个帮助子类中的某一个,并且你希望将哪个帮助子类是代理者这一信息局部化的时候。
结构
工厂方法模式主要包含以下四个角色:
- 产品(Product):定义了工厂方法所创建的对象的接口,也就是实际产品共有的接口或抽象类。
- 具体产品(Concrete Product):实现或继承了产品接口的具体类,这些类将由具体工厂创建,它们之间是继承关系。
- 创建者(Creator):声明了工厂方法,该方法返回一个产品类型的对象。创建者也可以定义一个工厂方法的默认实现,返回一个默认的具体产品对象。
- 具体创建者(Concrete Creator):重写或实现创建者中的工厂方法,以返回一个具体产品实例。
实现步骤
- 定义产品接口:所有具体产品类都应该实现这个接口,以便工厂方法可以返回这些类的实例。
- 创建具体产品类:实现产品接口的具体类。
- 定义创建者类:包含抽象的工厂方法,该方法应该返回一个产品接口的对象。创建者类通常会包含依赖于抽象产品的代码,而这些产品是由其子类通过工厂方法创建的。
- 实现具体创建者:为工厂方法提供实现,创建并返回具体产品实例。
优点
- 提高了系统的灵活性:因为工厂方法模式把具体产品的创建推迟到子类中,因此增加新的具体产品不需要修改现有类的代码,符合“开闭原则”。
- 扩展性强:在增加产品时只需要增加具体产品类和对应的具体工厂类,无需修改现有系统,符合“单一职责原则”。
缺点
- 可能导致类的个数增加:每增加一个具体产品类,就需要增加一个相应的具体工厂类,这会导致系统类的个数成倍增加,增加了系统的复杂度和管理难度。
示例
假设我们有一个日志记录器(Logger)的框架,日志记录器可以是文件日志记录器、数据库日志记录器或网络日志记录器等。在这个例子中,Logger就是产品,文件日志记录器、数据库日志记录器等就是具体产品,LoggerCreator是创建者,具体的文件日志记录器创建者、数据库日志记录器创建者等则是具体创建者。每个具体创建者都知道如何创建相应的具体产品。
工厂方法模式通过这种方式提供了一种将客户端代码从具体产品类中解耦的机制,使得增加新的产品类变得更加简单和灵活。下面是一个使用Java实现的工厂方法模式的简单例子,演示了如何构建一个简单的日志记录器框架。在这个框架中,我们有一个Logger
接口,它定义了日志记录功能;有两种具体的日志记录器,分别是FileLogger
和ConsoleLogger
;还有相应的工厂类,FileLoggerFactory
和ConsoleLoggerFactory
,它们负责创建具体的日志记录器实例。
1. 定义产品接口
public interface Logger {void log(String message);
}
2. 创建具体产品类
文件日志记录器:
public class FileLogger implements Logger {@Overridepublic void log(String message) {// 实现将消息记录到文件的逻辑System.out.println("Logging to a file: " + message);}
}
控制台日志记录器:
public class ConsoleLogger implements Logger {@Overridepublic void log(String message) {// 实现在控制台输出日志的逻辑System.out.println("Logging to the console: " + message);}
}
3. 定义创建者(工厂)接口
public abstract class LoggerFactory {public abstract Logger createLogger();
}
4. 实现具体创建者
文件日志工厂:
public class FileLoggerFactory extends LoggerFactory {@Overridepublic Logger createLogger() {// 返回一个新的文件日志记录器对象return new FileLogger();}
}
控制台日志工厂:
public class ConsoleLoggerFactory extends LoggerFactory {@Overridepublic Logger createLogger() {// 返回一个新的控制台日志记录器对象return new ConsoleLogger();}
}
5. 客户端代码
public class FactoryMethodDemo {public static void main(String[] args) {LoggerFactory factory;Logger logger;// 创建文件日志记录器factory = new FileLoggerFactory();logger = factory.createLogger();logger.log("This is a message to the file logger.");// 创建控制台日志记录器factory = new ConsoleLoggerFactory();logger = factory.createLogger();logger.log("This is a message to the console logger.");}
}
在这个例子中,LoggerFactory
充当创建者的角色,定义了一个抽象的createLogger
方法,用于返回一个Logger
对象。FileLoggerFactory
和ConsoleLoggerFactory
是具体的创建者,它们实现了createLogger
方法,分别用于创建FileLogger
和ConsoleLogger
对象。这样,客户端代码就可以在不直接实例化日志记录器对象的情况下,通过工厂来获取日志记录器实例,从而实现了客户端代码和具体产品类之间的解耦。
三、抽象工厂模式
抽象工厂模式(Abstract Factory Pattern)是一种创建型设计模式,它提供了一种创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。这个模式的关键点是多个工厂类,这些工厂类负责创建一系列相关的对象。抽象工厂模式通常用于管理产品族的概念,并帮助构建组件库或框架。
抽象工厂模式的组成部分
- 抽象工厂(Abstract Factory):提供一个接口,用于创建相关的对象家族。
- 具体工厂(Concrete Factory):实现抽象工厂的接口,负责创建具体的产品实例。
- 抽象产品(Abstract Product):为一类产品对象声明一个接口。
- 具体产品(Concrete Product):实现抽象产品角色所定义的接口。
优点
- 增加新的具体工厂和产品族很方便,无需修改已有的系统,符合开闭原则。
- 隔离了具体类的生成,使客户端不需要知道什么被创建。
缺点
- 添加新的产品对象到现有的工厂需要修改抽象工厂的接口,这将涉及到抽象工厂类以及所有的子类的修改,违反了开闭原则。
Java代码示例
假设我们有一个用于创建UI控件的框架,我们可以用抽象工厂模式来为不同的操作系统创建不同的UI控件。
首先,定义抽象工厂和抽象产品:
interface GUIFactory {Button createButton();Checkbox createCheckbox();
}interface Button {void paint();
}interface Checkbox {void paint();
}
然后,实现具体的工厂和产品:
class WinFactory implements GUIFactory {public Button createButton() {return new WinButton();}public Checkbox createCheckbox() {return new WinCheckbox();}
}class MacFactory implements GUIFactory {public Button createButton() {return new MacButton();}public Checkbox createCheckbox() {return new MacCheckbox();}
}class WinButton implements Button {public void paint() {System.out.println("Render a button in a Windows style.");}
}class MacButton implements Button {public void paint() {System.out.println("Render a button in a Mac style.");}
}class WinCheckbox implements Checkbox {public void paint() {System.out.println("Render a checkbox in a Windows style.");}
}class MacCheckbox implements Checkbox {public void paint() {System.out.println("Render a checkbox in a Mac style.");}
}
最后,客户端代码可以根据需要选择使用哪个具体的工厂实例:
class Application {private Button button;private Checkbox checkbox;public Application(GUIFactory factory) {button = factory.createButton();checkbox = factory.createCheckbox();}public void paint() {button.paint();checkbox.paint();}
}public class Demo {public static void main(String[] args) {Application app = new Application(new WinFactory());app.paint();}
}
这个示例展示了如何使用抽象工厂模式来解耦客户端和具体类的实例化过程,使客户端代码能够无需修改就可以使用不同的产品家族。
相关文章:
设计模式之旅:工厂模式全方位解析
简介 设计模式中与工厂模式相关的主要有三种,它们分别是: 简单工厂模式(Simple Factory):这不是GoF(四人帮,设计模式的开创者)定义的标准模式,但被广泛认为是工厂模式的…...
大数据时代的生物信息学:挖掘生命数据,揭示生命奥秘
在当今科技日新月异的时代,大数据如同一座蕴藏无尽宝藏的矿山,而生物信息学则是那把锐利的探矿锤,精准有力地敲击着这座“生命之矿”,揭示出隐藏在其深处的生命奥秘。随着基因测序技术的飞速进步与广泛应用,生物医学领…...
微信小程序开发【从入门到精通】——页面导航
👨💻个人主页:开发者-曼亿点 👨💻 hallo 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅! 👨💻 本文由 曼亿点 原创 👨💻 收录于专栏:…...
嵌入式|蓝桥杯STM32G431(HAL库开发)——CT117E学习笔记15:PWM输出
系列文章目录 嵌入式|蓝桥杯STM32G431(HAL库开发)——CT117E学习笔记01:赛事介绍与硬件平台 嵌入式|蓝桥杯STM32G431(HAL库开发)——CT117E学习笔记02:开发环境安装 嵌入式|蓝桥杯STM32G431(…...
SQLite中的隔离(八)
返回:SQLite—系列文章目录 上一篇:SQLite版本3中的文件锁定和并发(七) 下一篇:SQLite 查询优化器概述(九) 数据库的“isolation”属性确定何时对 一个操作的数据库对其他并发操作可见。 数据库连接之…...
Zabbix6 - Centos7部署Grafana可视化图形监控系统配置手册手册
Zabbix6 - Centos7部署Grafana可视化图形监控系统配置手册手册 概述: Grafana是一个开源的数据可视化和监控平台。其特点: 1)丰富的可视化显示插件,包括热图、折线图、饼图,表格等; 2)支持多数据…...
Electron无边框自定义窗口拖动
最近使用了electron框架,发现如果自定义拖动是比较实用的;特别是定制化比较高的项目,如果单纯的使用-webkit-app-region: drag;会让鼠标事件无法触发; 过程中发现问题: 1.windows缩放不是100%后设置偏移界面会缩放,感觉像吹起的气…...
vue3+echarts:echarts地图打点显示的样式
colorStops是打点的颜色和呼吸灯、label为show是打点是否显示数据、rich里cnNum是自定义的过滤模板用来改写显示数据的样式 series: [{type: "effectScatter",coordinateSystem: "geo",rippleEffect: {brushType: "stroke",},showEffectOn: &quo…...
vue3从精通到入门7:ref系列
Vue 3 的 Ref 是一个集合,包括多个与响应式引用相关的功能,这些功能共同构成了 Vue 3 响应式系统的重要组成部分。以下是更全面的介绍: 1.ref ref 接受一个内部值并返回一个响应式且可变的 ref 对象。这个对象具有一个 .value 属性…...
灵动翻译音频文件字幕提取及翻译;剪映视频添加字幕
参考:视频音频下载工具 https://tuberipper.com/21/save/mp3 1、灵动翻译音频文件字幕提取及翻译 灵动翻译可以直接chorme浏览器插件安装: 点击使用,可以上传音频文件 上传后自动翻译,然后点击译文即可翻译成中文,…...
在Gitee上创建新仓库
1. 登录到你的Gitee账户。 2. 在Gitee首页或仓库页面,点击“新建仓库”按钮。 3. 填写仓库名称、描述(可选)、选择仓库是否公开等信息。 4. 点击“创建仓库”按钮完成创建。 2. 本地代码连接到远程仓库 假设你已经在本地有一个项目&#…...
linux 配置NFS
1、NFS简介 NFS 是Network File System的缩写,即⽹络⽂件系统。NFS 的基本原则是“容许不同的客户 端及服务端通过⼀组RPC分享相同的⽂件系统”,它是独⽴于操作系统,容许不同硬件及操作 系统的系统共同进⾏⽂件的分享。 NFS在⽂件传送或信息…...
大疆御Pro(一代)更换晓spark摄像头评测
御Pro是17年的老机器,除了摄像头有点拉跨,续航、抗风、操作性在大疆民用系列里面算是数得上的。 机缘巧合,手头有几个御的空镜头(里面的芯片已经去掉了),还有几个晓的摄像头(只有芯片࿰…...
【小技巧】gitlab怎么在每次git push的时候不用输入账号密码?使用 SSH 密钥 的原理是什么?
1. gitlab怎么在每次git push的时候不用输入账号密码? 要在每次执行 git push 时避免输入 GitLab 的账号和密码,你可以通过以下几种方法实现: 使用 SSH 密钥:这是最常用的方法,通过生成 SSH 密钥并将其添加到 GitLab …...
笔记: JavaSE day15 笔记
第十五天课堂笔记 数组 可变长参数★★★ 方法 : 返回值类型 方法名(参数类型 参数名 , 参数类型 … 可变长参数名){}方法体 : 变长参数 相当于一个数组一个数组最多只能有一个可变长参数, 并放到列表的最后parameter : 方法参数 数组相关算法★★ 冒泡排序 由小到大: 从前…...
【Golang星辰图】数据处理的航海家:征服数据海洋的航行工具
数据处理的建筑师:用Go语言中构建稳固的数据分析建筑物 前言 数据处理和分析是现代计算机科学中的关键任务之一,而Go语言作为一门现代化的编程语言,也需要强大的数据处理和分析库来支持其在这一领域的应用。本文将介绍几款优秀的数据处理和…...
容器网络测试关键问题
资料问题 主要影响客户体验, 低级问题. 类似于单词拼写错误, 用词有歧义,等。 另一点是,我们的用户文档,主要偏向于技术向的描述,各种参数功能罗列。友商有比较好的最佳实践操作说明。我们后面也会都增加这样的最佳实践。golang o…...
6、Cocos Creator 2D 渲染组件:Sprite 组件
Sprite 组件 Sprite(精灵)是 2D/3D 游戏最常见的显示图像的方式,在节点上添加 Sprite 组件,就可以在场景中显示项目资源中的图片。 属性功能说明Type渲染模式,包括普通(Simple)、九宫格&#x…...
算法沉淀——动态规划篇(子数组系列问题(上))
算法沉淀——动态规划篇(子数组系列问题(上)) 前言一、最大子数组和二、环形子数组的最大和三、乘积最大子数组四、乘积为正数的最长子数组长度 前言 几乎所有的动态规划问题大致可分为以下5个步骤,后续所有问题分析都…...
通知中心架构:打造高效沟通平台,提升信息传递效率
随着信息技术的快速发展,通知中心架构作为一种关键的沟通工具,正逐渐成为各类应用和系统中必不可少的组成部分。本文将深入探讨通知中心架构的意义、设计原则以及在实际场景中的应用。 ### 什么是通知中心架构? 通知中心架构是指通过集中管…...
【Arduino使用SNR9816TTS模块教程】
【Arduino使用SNR9816TTS模块教程】 1.前言2. 硬件连接3. Arduino代码3.1 环境配置3.2 Arduino源码 4. 调试步骤5. 总结 1.前言 在今天的教程中,我们将详细介绍如何使用Arduino IDE开发ESP32C3与汕头新纳捷科技有限公司生产的SNR9816TTS中文人声语音合成模块进行交…...
牛客练习赛123(A,B,C,D)
牛客挑战赛,练习赛和小白月赛周赛不是一种东西。这玩意跟CF的div12差不多难度。而且找不到题解。所以决定不等题解补题了,直接写题解了。 比赛链接 光速签到下班,rk。感觉E可能能补掉,看情况补吧。 B题感觉之前考了两次&#x…...
docker部署-RabbitMq
1. 参考 RabbitMq官网 docker官网 2. 拉取镜像 这里改为自己需要的版本即可,下面容器也需要同理修改 docker pull rabbitmq:3.12-management3. 运行容器 docker run \ --namemy-rabbitmq-01 \ -p 5672:5672 \ -p 15672:15672 \ -d \ --restart always \ -…...
【智能算法】蜜獾算法(HBA)原理及实现
目录 1.背景2.算法原理2.1算法思想2.2算法过程 3.结果展示4.参考文献 1.背景 2021年,FA Hashim等人受到自然界中蜜獾狩猎行为启发,提出了蜜獾算法((Honey Badger Algorithm,HBA)。 2.算法原理 2.1算法思想 蜜獾以其…...
9、鸿蒙学习-开发及引用静态共享包(API 9)
HAR(Harmony Archive)是静态共享包,可以包含代码、C库、资源和配置文件。通过HAR可以实现多个模块或多个工程共享ArkUI组件、资源等相关代码。HAR不同于HAP,不能独立安装运行在设备上,只能作为应用模块的依赖项被引用。…...
[Pytorch]:PyTorch中张量乘法大全
在 PyTorch 中,有多种方法可以执行张量之间的乘法。这里列出了一些常见的乘法操作: 总结: 逐元素乘法:*ortorch.mul()矩阵乘法:ortorch.mm()ortorch.matmul()点积:torch.Tensor.dot()批量矩阵乘法ÿ…...
【软考】防火墙技术
目录 1. 概念2. 包过滤防火墙3. 应用代理网关防火墙4. 状态检测技术防火墙 1. 概念 1.防火墙(Firewall)是建立在内外网络边界上的过滤封锁机制,它认为内部网络是安全和可信赖的,而外部网络是不安全和不可信赖的。2.防火墙的作用是防止不希望的、未经授权…...
OpenHarmony实战:Makefile方式组织编译的库移植
以yxml库为例,其移植过程如下文所示。 源码获取 从仓库获取yxml源码,其目录结构如下表: 表1 源码目录结构 名称描述yxml/bench/benchmark相关代码yxml/test/测试输入输出文件,及测试脚本yxml/Makefile编译组织文件yxml/.gitat…...
嵌入式C语言--GPT通用定时器
嵌入式C语言–GPT通用定时器 嵌入式C语言--GPT通用定时器 嵌入式C语言--GPT通用定时器一. GPT基本概念二. GPT的作用三. GPT通道的四个状态四. Continuous/One-Shot模式3.1)Continuous模式3.2)One-Shot模式 一. GPT基本概念 GPT即General Purpose Timer…...
『Apisix系列』破局传统架构:探索新一代微服务体系下的API管理新范式与最佳实践
一、『Apisix安装部署』 🚀 1.1 手把手教你从零部署APISIX高性能API网关 二、『Apisix入门篇』 🚀 2.1 从零到一掌握Apache APISIX:架构解析与实战指南 三、『Apisix进阶篇』 🚀 3.1 动态负载均衡:APISIX的实战演练…...
免费网站建设市场/怎么做推广让别人主动加我
计算机二级access题库答案在文末1.在Access数据库中,一个关系就是一个【 A】。A)二维表 B)记录C)字段 D)数据库 综合数据2. 设有部门和员工两个实体,每个员工只能属于一个部门,一个部门可以有多名员工,则部门与…...
黄色的html代码/seo下拉优化
1.下载安装qrcodejs2包 npm i qrcodejs2 2.导入 import QRCode from "qrcodejs2"; 3.html <div class"qrcode" id"qrcode"></div> //class是我的样式可以忽略,但是id一定要下 4.使用,以下是我的代码&…...
马鞍山网站建设设计/东莞seoseo关键词排名优化
一般情况下,我们使用的是nvidia -smi 来监控,但不美观且信息冗余,此处使用gpustat来监控,可直接安装:pip install gpustat 然后我们输入gpustat即可得到: 但此时无法实时获取,使用以下命令即可…...
东莞最新疫情最新消息/短视频seo搜索优化
今天给大侠带来如何写好状态机,状态机是逻辑设计的重要内容,状态机的设计水平直接反应工程师的逻辑功底,所以很多公司在硬件工程师及逻辑工程师面试中,状态机设计几乎是必选题目。本篇在引入状态机设计思想的基础上,重…...
网络工程有限公司/品牌企业seo咨询
965. 单值二叉树 难度简单69 如果二叉树每个节点都具有相同的值,那么该二叉树就是单值二叉树。 只有给定的树是单值二叉树时,才返回 true;否则返回 false。 示例 1: 输入:[1,1,1,1,1,null,1] 输出:true示例…...