【Java开发】设计模式 02:工厂模式
1 工厂模式介绍
工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。
1.1 场景
目的:使用工厂代替new操作,使代码结构清晰,有效地封装变化;
适用:在任何需要生成复杂对象的地方,都可以使用工厂方法模式
日志记录器:记录可能记录到本地硬盘、系统事件、远程服务器等,用户可以选择记录日志到什么地方。
数据库访问,当用户不知道最后系统采用哪一类数据库,以及数据库可能有变化时。
设计一个连接服务器的框架,需要三个协议,”POP3”、”IMAP”、”HTTP”,可以把这三个作为产品类,共同实现一个接口。
优点:
一个调用者想创建一个对象,只要知道其名称就可以了。
扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。
屏蔽产品的具体实现,调用者只关心产品的接口。
缺点:每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。
根据迪米特法则,应该尽可能地少与其他类进行交互,所以可以将那些需要频繁出现的对象创建,封装到一个工厂类中。当需要对象时,直接调用工厂类中的工厂方法来生成对象,这样,就算类出现了变动,只需要修改工厂中的代码即可,而不是大面积地进行修改。同时,可能某些对象的创建并不只是一个 new 就可以搞定,可能还需要更多的步骤来准备构造方法需要的参数。
1.2 简单工厂模式实现
简单工厂其实不是一个标准的的设计模式。GOF 23 种设计模式中只有「工厂方法模式」与「抽象工厂模式」。简单工厂模式可以看为工厂方法模式的一种特例,为了统一整理学习,就都归为工厂模式。
下面以水果工厂生产水果为例介绍普通工厂模式:

📌 1.定义水果类
//水果类
public class Fruit {private final String name;public Fruit(String name){this.name = name;}
}
📌 2.定义苹果类和橙子类
//苹果类
public class Apple extends Fruit{public Apple() {super("苹果");}
}
//橙子类
public class Orange extends Fruit{public Orange() {super("橙子");}
}
📌 3.定义水果工厂
//水果工厂
public class FruitFactory{/*** 这里就直接使用静态方法根据指定类型进行创建* @param type 水果类型* @return 对应的水果对象*/public static Fruit getFruit(String type){switch (type){case "苹果":return new Apple();case "橙子":return new Orange();default:return null;}}
}
📌 4.调用
// 直接问工厂要,而不是自己去创建
Apple apple = (Apple) FruitFactory.getFruit("苹果");
Orange orange = (Orange) FruitFactory.getFruit("橙子");
📢 总结
工厂类是整个简单工厂模式的关键。包含了必要的逻辑判断,根据外界给定的信息,决定究竟应该创建哪个具体类的对象。通过使用工厂类,外界可以从直接创建具体产品对象的尴尬局面摆脱出来,仅仅需要负责“消费”对象就可以了。而不必管这些对象究竟如何创建及如何组织的。明确了各自的职责和权利,有利于整个软件体系结构的优化。
不过这样还是有一些问题,比如开闭原则,一个软件实体,比如类、模块和函数应该对扩展开放,对修改关闭。此时如果需要新增一种水果,比如桃子,那么就得去修改工厂提供的工厂方法了,这样是不太符合开闭原则的。因为工厂实际上是针对于调用方提供的,所以应该尽可能对修改关闭。
2 工厂方法模式
我们就利用对扩展开放,对修改关闭的性质,将简单工厂模式改进为工厂方法模式,既然不让改,那么就看看如何去使用扩展的形式。
这里还是以水果工厂生产水果介绍工厂方法模式:

📌 1.定义水果工厂
import com.yinyu.study.factory.simple.Fruit;/*** 水果工厂* <p>将水果工厂抽象为抽象类,添加泛型 T 由子类指定水果类型*/
public abstract class FruitFactory<T extends Fruit> {/*** 不同的水果工厂,通过此方法生产不同的水果* @return 水果对象*/public abstract T getFruit();
}
📌 2.定义苹果工厂和橙子工厂
import com.yinyu.study.factory.simple.Apple;public class AppleFactory extends FruitFactory<Apple>{/*** 获取苹果对象* @return 苹果对象*/@Overridepublic Apple getFruit() {return new Apple();}
}
import com.yinyu.study.factory.simple.Orange;public class OrangeFactory extends FruitFactory<Orange>{/*** 获取橙子对象* @return 橙子对象*/@Overridepublic Orange getFruit() {return new Orange();}
}
这样,就可以使用不同类型的工厂来生产不同类型的水果了,如果新增了水果类型,直接创建一个新的工厂类就行,不需要修改之前已经编写好的内容。
📌 3.调用
// 使用对应的工厂生产对应的对象
Apple apple = new AppleFactory().getFruit();
Orange orange = new OrangeFactory().getFruit();
这样实现了工厂方法模式,通过工厂来屏蔽对象的创建细节,使用者只需要关心如何去使用对象即可。
📢 总结
由于使用了面向对象的多态性,工厂方法模式保持了简单工厂模式的优点,而且克服了它的缺点。
在工厂方法模式中,核心的工厂类不再负责所有产品的创建,而是将具体创建工作交给子类去做。这个核心类仅仅负责给出具体工厂必须实现的接口,而不负责产品类被实例化这种细节,这使得工厂方法模式可以允许系统在不修改工厂角色的情况下引进新产品。
3 抽象工厂模式
工厂方法模式通过引入工厂等级结构,解决了简单工厂模式中工厂类职责太重的问题,但由于工厂方法模式中的每个工厂只生产一类产品,可能会导致系统中存在大量的工厂类,势必会增加系统的开销。此时,我们可以考虑将一些相关的产品组成一个“产品族”,由同一个工厂来统一生产,这就是抽象工厂模式的基本思想。
为创建一组相关或相互依赖的对象提供一个接口,而且无需指定他们的具体类,抽象工厂(Abstract Factory)模式,又称工具箱(Kit 或 Toolkit)模式。
这里以云南工厂生产水果介绍抽象工厂模式:

📌 1.定义香蕉和芒果接口
public interface Banana {void bananaInfo();
}
public interface Mango {void mangoInfo();
}
📌 2.定义云南香蕉和云南芒果工厂
public class YunnanBanana implements Banana{@Overridepublic void bananaInfo() {System.out.println("云南香蕉");}
}
public class YunnanMango implements Mango{@Overridepublic void mangoInfo() {System.out.println("云南芒果");}
}
📌 3.定义工厂接口生产香蕉和芒果
public interface Factory {Banana proBanana();Mango proMango();
}
📌 4.定义云南工厂类实现工厂接口
public class YunnanFactory implements Factory{@Overridepublic Banana proBanana() {return new YunnanBanana();}@Overridepublic Mango proMango() {return new YunnanMango();}
}
📌 5.调用
Factory factory = new YunnanFactory();YunnanBanana banana = (YunnanBanana) factory.proBanana();
YunnanMango mango = (YunnanMango) factory.proMango();banana.bananaInfo();
mango.mangoInfo();
📢 总结
云南工厂类似一个产品族,它可以生产云南香蕉和云南芒果,那么也可以很方便地创建一个广西工厂,用来生产广西香蕉和广西芒果;不过倘若各工厂要生产一件新的水果,那么工厂接口和各具体工厂都将进行修改,如此会造成较大麻烦。
相关文章:

【Java开发】设计模式 02:工厂模式
1 工厂模式介绍工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使…...

合并两个链表(自定义位置合并与有序合并)LeetCode--OJ题详解
图片: csdn 自定义位置合并 问题: 给两个链表 list1 和 list2 ,它们包含的元素分别为 n 个和 m 个。 请你将 list1 中 下标从 a 到 b 的全部节点都删除,并将list2 接在被删除节点 的位置。 比如: 输入:list1 [1…...
Java编程问题总结
Java编程问题总结 整理自 https://github.com/giantray/stackoverflow-java-top-qa 基础语法 将InputStream转换为String apache commons-io String content IOUtils.toString(new FileInputStream(file), StandardCharsets.UTF_8); //String value FileUtils.readFileT…...
binutils工具集——objcopy的用法
以下内容源于网络资源的学习与整理,如有侵权请告知删除。 一、工具简介 objcopy主要用来转换目标文件的格式。 在实际开发中,我们会用该工具进行格式转换与内容删除。 (1)在链接完成后,将elf格式的.out文件转化为bi…...

Windows使用Stable Diffusion时遇到的各种问题和知识点整理(更新中...)
Stable Diffusion安装完成后,在使用过程中会出现卡死、文件不存在等问题,在本文中将把遇到的问题陆续记录下来,有兴趣的朋友可以参考。 如果要了解如何安装sd,则参考本文《Windows安装Stable Diffusion WebUI及问题解决记录》。如…...

MySQL workbench基本查询语句
1.查询所有字段所有记录 SELECT * FROM world.city; select 表示查询;“*” 称为通配符,也称为“标配符”。表示将表中所有的字段都查询出来;from 表示从哪里查询;world.city 表示名为world的数据库中的city表; 上面…...

软件测试详解
文章目录一、软件危机(一)概念(二)产生软件危机的原因(三)消除软件危机的途径二、软件过程模型(一)软件生命周期概念(二)软件开发模型1. 瀑布模型2. 螺旋模型…...

YOLOS学习记录
在前面,博主已经完成了YOLOS项目的部署与调试任务,并在博主自己构造的数据集上进行了实验,实验结果表明效果并不显著,其实这一点并不意外,反而是在情理之中。众所周知,Transformer一直以来作为NLP领域的带头…...

数组边遍历(for循环)边删除为什么删不干净 及三种实现删除的方法
文章目录1、为什么删不干净倒序删迭代器lambda表达式删除为什么说数组边for循环遍历边删除会出现删不干净的情况1、为什么删不干净 先写一个例子:可以先猜一下控制台会打印出什么内容? public class removeIterator {public static void main(String[]…...

环境配置之Keepass
前言很久以前,就有了想要一个自己密码管理器的念头。毕竟,即使浏览器能记住各个网站的账号密码,但是在登录单独客户端的时候,仍然要翻找密码。为了省事,也曾经是一个密码走天下。然后被劫持了QQ给同学发黄色小网站&…...

Java 电话号码的组合
电话号码的字母组合中等给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。示例 1:输入:digits "23…...

MATLAB——将直接型转化为并联型和级联型
题目1(IIR): 已知一个系统的传递函数为: H(z)8−4z−111z−2−2z−31−1.25z−10.75z−2−0.125z−3H(z)\frac{8-4z^{-1}11z^{-2}-2z^{-3}}{1-1.25z^{-1}0.75z^{-2}-0.125z^{-3}}H(z)…...

.NET Framework .NET Core与 .NET 的区别
我们在创建C#程序时,经常会看到目标框架以下的选项,那么究竟有什么区别? 首先 .NET是一种用于构建多种应用的免费开源开发平台,可以使用多种语言,编辑器和库开发Web应用、Web API和微服务、云中的无服务器函数、云原生应用、移动应用、桌面应用、Windows WPF、Windows窗体…...

carla与ros2的自动驾驶算法-planning与control算法开发与仿真
欢迎仪式 carla与ros2的自动驾驶算法-planning与control算法开发与仿真欢迎大家来到自动驾驶Player(L5Player)的自动驾驶算法与仿真空间,在这个空间我们将一起完成这些事情: 控制算法构建基础模块并仿真调试:PID、LQR、Stanley 、MPC、滑膜控…...

corn表达式
简单理解corn表达式:在使用定时调度任务的时候,我们最常用的,就是cron表达式了。通过cron表达式来指定任务在某个时间点或者周期性的执行。cron表达式配置起来简洁方便,无论是Spring的Scheduled还是用Quartz框架,都支持…...

推荐系统中对抗性机器学习-文献综述与未来发展整理分享
对抗学习是一种机器学习技术,旨在通过提供欺骗性输入来欺骗模型。最常见的原因是导致机器学习模型出现故障。大多数机器学习技术旨在处理特定的问题集,其中从相同的统计分布(IID)生成训练和测试数据。当这些模型应用于现实世界时&…...

Proteus8.15安装教程
1、解压Proteus8.15 安装包,然后双击进去,找到setup文件,右键,以管理员身份运行。 2、需要安装一些插件,点击“next”。把插件安装完成。 点击“finfish” 点击“install” 点击“Cancel” 3、如果没有上面步骤&…...

Shell 基本运算符
Shell 和其他编程语言一样,支持多种运算符,包括: 算数运算符关系运算符布尔运算符字符串运算符文件测试运算符 原生bash不支持简单的数学运算,但是可以通过其他命令来实现,例如 awk 和 expr,expr 最常用。…...

Linux基础命令-sed流编辑器
Linux三剑客-grep命令 Sed 一. 命令介绍 先到帮助文档中查看命令的描述信息 NAME sed - stream editor for filtering and transforming text sed命令是操作、过滤和转换文本内容的强大工具,常用功能包括结合正则表达式对文件实现快速增删改查,其中查询…...
C语言笔试题(1)
#include <stdio.h> #include <stdlib.h> #include <string.h> void getmemory(char *p) { p(char *) malloc(100); strcpy(p,“hello world”); } int main(void) { char *strNULL; getmemory(str); printf(“%s/n”,str); free(str); return 0; } 上述程序…...
后进先出(LIFO)详解
LIFO 是 Last In, First Out 的缩写,中文译为后进先出。这是一种数据结构的工作原则,类似于一摞盘子或一叠书本: 最后放进去的元素最先出来 -想象往筒状容器里放盘子: (1)你放进的最后一个盘子(…...
日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする
日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする 1、前言(1)情况说明(2)工程师的信仰2、知识点(1) にする1,接续:名词+にする2,接续:疑问词+にする3,(A)は(B)にする。(2)復習:(1)复习句子(2)ために & ように(3)そう(4)にする3、…...

盘古信息PCB行业解决方案:以全域场景重构,激活智造新未来
一、破局:PCB行业的时代之问 在数字经济蓬勃发展的浪潮中,PCB(印制电路板)作为 “电子产品之母”,其重要性愈发凸显。随着 5G、人工智能等新兴技术的加速渗透,PCB行业面临着前所未有的挑战与机遇。产品迭代…...

相机Camera日志实例分析之二:相机Camx【专业模式开启直方图拍照】单帧流程日志详解
【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了: 这一篇我们开始讲: 目录 一、场景操作步骤 二、日志基础关键字分级如下 三、场景日志如下: 一、场景操作步骤 操作步…...
Linux云原生安全:零信任架构与机密计算
Linux云原生安全:零信任架构与机密计算 构建坚不可摧的云原生防御体系 引言:云原生安全的范式革命 随着云原生技术的普及,安全边界正在从传统的网络边界向工作负载内部转移。Gartner预测,到2025年,零信任架构将成为超…...

Springboot社区养老保险系统小程序
一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,社区养老保险系统小程序被用户普遍使用,为方…...
Fabric V2.5 通用溯源系统——增加图片上传与下载功能
fabric-trace项目在发布一年后,部署量已突破1000次,为支持更多场景,现新增支持图片信息上链,本文对图片上传、下载功能代码进行梳理,包含智能合约、后端、前端部分。 一、智能合约修改 为了增加图片信息上链溯源,需要对底层数据结构进行修改,在此对智能合约中的农产品数…...

C++:多态机制详解
目录 一. 多态的概念 1.静态多态(编译时多态) 二.动态多态的定义及实现 1.多态的构成条件 2.虚函数 3.虚函数的重写/覆盖 4.虚函数重写的一些其他问题 1).协变 2).析构函数的重写 5.override 和 final关键字 1&#…...

Golang——6、指针和结构体
指针和结构体 1、指针1.1、指针地址和指针类型1.2、指针取值1.3、new和make 2、结构体2.1、type关键字的使用2.2、结构体的定义和初始化2.3、结构体方法和接收者2.4、给任意类型添加方法2.5、结构体的匿名字段2.6、嵌套结构体2.7、嵌套匿名结构体2.8、结构体的继承 3、结构体与…...

从“安全密码”到测试体系:Gitee Test 赋能关键领域软件质量保障
关键领域软件测试的"安全密码":Gitee Test如何破解行业痛点 在数字化浪潮席卷全球的今天,软件系统已成为国家关键领域的"神经中枢"。从国防军工到能源电力,从金融交易到交通管控,这些关乎国计民生的关键领域…...