重读 Java 设计模式: 深入探讨工厂模式,创建对象的灵活性与可维护性

引言

今天我们来继续学习创建型设计模式中的工厂模式。在软件开发中,工厂模式是一种常见的设计模式,旨在提供一种灵活、可扩展的方式来创建对象实例。工厂模式通常分为简单工厂模式和抽象工厂模式两种主要形式,它们在不同情境下各具优势,可以帮助开发人员更好地管理对象的创建过程,并提高代码的可维护性和可扩展性。

本篇文章,我们换个新思路来讲述(后续文章都按照此思路来):

  • 举例子讲述从无模式 ==> 简单工厂模式 ==> 抽象工厂模式的应用。
  • 探索 Spring 框架中对工厂模式的应用。
  • 从零开始填充我们的设计百宝箱,包括面向对象基础面向对象设计原则面向对象设计模式
  • 针对工厂模式,我们产生了哪些思考?有什么问题?它们的答案是什么?

一、新能源汽车的发展

最近几年,新能源电车异军突起,掀起来又一条经济赛道!各大公司都进军这条赛道,建造不同品牌的新能源电车。今天我们就以这条案例来讲述下工厂模式。

1、无模式状况下

如果不引入任何设计模式的话,我们实现用户买车,厂商进行制造的流程应该是下面这样子的:

package com.markus.desgin.mode.creational.factory.none;/*** @author: markus* @date: 2024/3/16 9:59 PM* @Description: 无模式* @Blog: https://markuszhang.com* It's my honor to share what I've learned with you!*/
public class NonePatterClient {public static void main(String[] args) {// 买小米汽车Car car = orderCar("xiaomi");// 买比亚迪汽车car = orderCar("byd");}private static Car orderCar(String type) {Car car;if ("xiaomi".equals(type)) {car = new XiaoMiCar();} else if ("byd".equals(type)) {car = new XiaoMiCar();} else {throw new UnsupportedOperationException("不支持的汽车类型");}// 有了汽车厂商后,我们就开始安装对应的电池、轮子以及座椅(实际上很多程序,我这里就选三个作为举例)car.installBattery();car.installWheel();car.installSeat();// 将成品交付return car;}
}

接下来我们思考一下上述这段代码有什么问题?

显而易见,如果应用支持更多的汽车类型,orderCar() 方法就会不断的被修改。这不符合设计原则中的开闭原则(对扩展开放,对修改关闭)。

那么,我们如何改造一下上述代码呢?

2、简单工厂模式

在进行任何程序设计时,我们想要做到对扩展开放、对修改关闭的目的就只需要记住一点:分析程序变化和不变化的部分,将变化的分布抽离出去。

好了,记住上述理论后,我们开始着手对上述代码进行改造。

第一步:哪些是变化的部分?哪些是不变的部分?

image-20240316222730986

第二步:将变化的部分抽离出去。

package com.markus.desgin.mode.creational.factory.article;/*** @author: markus* @date: 2024/3/16 10:29 PM* @Description: 默认的汽车工厂* @Blog: https://markuszhang.com* It's my honor to share what I've learned with you!*/
public class DefaultCarFactory implements CarFactory {@Overridepublic Car createCar(CarType carType) {if (carType.equals(CarType.XIAOMI)) {return new XiaoMiCar();} else if (carType.equals(CarType.BYD)) {return new BYDCar();}throw new UnsupportedOperationException("其他汽车类型不支持创建!");}
}

第三步:将原有代码进行最终改造。

package com.markus.desgin.mode.creational.factory.article;/*** @author: markus* @date: 2024/3/16 10:37 PM* @Description: 简单工厂模式* @Blog: https://markuszhang.com* It's my honor to share what I've learned with you!*/
public class SimpleFactoryPattern {public static void main(String[] args) {// 买小米汽车Car car = orderCar(CarType.XIAOMI);// 买比亚迪汽车car = orderCar(CarType.BYD);}private static Car orderCar(CarType carType) {CarFactory carFactory = new DefaultCarFactory();Car car = carFactory.createCar(carType);// 有了汽车厂商后,我们就开始安装对应的电池、轮子以及座椅(实际上很多程序,我这里就选三个作为举例)car.installBattery();car.installWheel();car.installSeat();// 将成品交付return car;}
}

可以看出,不管后续再出现什么类型的汽车,orderCar() 方法的代码都不会进行修改,只需要在 DefaultCarFactory 类中进行扩展即可。

大家可能会说了:这不就是挪了个地方吗?

对,就是搬到了另一个地方,但是别忘了我们将这块的代码从客户端中抽象出来,它就可以服务于多个客户了,而不仅仅是当前这一个客户端。另外还有一个比较常见的问题:有些场景会把工厂方法定义为一个静态的方法。这是一个很常见的技巧,但这种与我们上述实现的方式对比有一个比较明显的劣势就是它不能通过继承来改变创建方法的行为。

到此我们就实现通过简单工厂方法设计对代码进行了一定的改造,使其具有更好的可扩展性。实际上,它并不是一个设计模式,反而像一种编程习惯,它定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类。

最后用一张 UML 图来展示下工厂方法的应用:

image-20240316225136746

此时,又出现了一种场景:每个汽车制造商觉得自己生产电池太困难,没有那么多精力投入,因此将电池的生产交给各个电池生产厂商进行生产,最终自己来完成组装。

3、抽象工厂模式

3.1 汽车

接口定义

package com.markus.desgin.mode.creational.factory.article.abstractfactory;/*** @author: markus* @date: 2024/3/16 10:00 PM* @Description:* @Blog: https://markuszhang.com* It's my honor to share what I've learned with you!*/
public interface Car {void installWheel();void installSeat();void installBattery();
}

小米基础版汽车

package com.markus.desgin.mode.creational.factory.article.abstractfactory;import com.markus.desgin.mode.creational.factory.article.battery.BatteryFactory;/*** @author: markus* @date: 2024/3/16 11:29 PM* @Description:* @Blog: https://markuszhang.com* It's my honor to share what I've learned with you!*/
public class XiaomiBasicCar extends AbstractCar {public XiaomiBasicCar(BatteryFactory batteryFactory) {super(batteryFactory);}@Overridepublic void installWheel() {System.out.println("安装小米基础版轮子");}@Overridepublic void installSeat() {System.out.println("安装小米基础版座椅");}
}

小米旗舰版汽车

package com.markus.desgin.mode.creational.factory.article.abstractfactory;import com.markus.desgin.mode.creational.factory.article.battery.BatteryFactory;/*** @author: markus* @date: 2024/3/16 11:29 PM* @Description:* @Blog: https://markuszhang.com* It's my honor to share what I've learned with you!*/
public class XiaomiFlagshipCar extends AbstractCar {public XiaomiFlagshipCar(BatteryFactory batteryFactory) {super(batteryFactory);}@Overridepublic void installWheel() {System.out.println("安装小米旗舰版轮子");}@Overridepublic void installSeat() {System.out.println("安装小米旗舰版座椅");}
}

比亚迪基础版汽车

package com.markus.desgin.mode.creational.factory.article.abstractfactory;import com.markus.desgin.mode.creational.factory.article.battery.BatteryFactory;/*** @author: markus* @date: 2024/3/16 11:29 PM* @Description:* @Blog: https://markuszhang.com* It's my honor to share what I've learned with you!*/
public class BYDBasicCar extends AbstractCar {public BYDBasicCar(BatteryFactory batteryFactory) {super(batteryFactory);}@Overridepublic void installWheel() {System.out.println("安装比亚迪基础版轮子");}@Overridepublic void installSeat() {System.out.println("安装比亚迪基础版座椅");}
}

比亚迪旗舰版汽车

package com.markus.desgin.mode.creational.factory.article.abstractfactory;import com.markus.desgin.mode.creational.factory.article.battery.BatteryFactory;/*** @author: markus* @date: 2024/3/16 11:29 PM* @Description:* @Blog: https://markuszhang.com* It's my honor to share what I've learned with you!*/
public class BYDFlagshipCar extends AbstractCar {public BYDFlagshipCar(BatteryFactory batteryFactory) {super(batteryFactory);}@Overridepublic void installWheel() {System.out.println("安装比亚迪旗舰版轮子");}@Overridepublic void installSeat() {System.out.println("安装比亚迪旗舰版座椅");}
}
3.2 电池制造厂定义
package com.markus.desgin.mode.creational.factory.article.battery;/*** @author: markus* @date: 2024/3/16 11:05 PM* @Description:* @Blog: https://markuszhang.com* It's my honor to share what I've learned with you!*/
public interface BatteryFactory {String productBattery();
}

宁德时代

package com.markus.desgin.mode.creational.factory.article.battery;/*** @author: markus* @date: 2024/3/16 11:05 PM* @Description: 宁德时代* @Blog: https://markuszhang.com* It's my honor to share what I've learned with you!*/
public class CATL implements BatteryFactory {@Overridepublic String productBattery() {return "宁德时代电池";}
}

东芝

package com.markus.desgin.mode.creational.factory.article.battery;/*** @author: markus* @date: 2024/3/16 11:06 PM* @Description: 东芝* @Blog: https://markuszhang.com* It's my honor to share what I've learned with you!*/
public class Toshiba implements BatteryFactory {@Overridepublic String productBattery() {return "东芝电池";}
}
3.3 汽车制造厂定义

小米工厂

package com.markus.desgin.mode.creational.factory.article.abstractfactory;import com.markus.desgin.mode.creational.factory.article.battery.BatteryFactory;
import com.markus.desgin.mode.creational.factory.article.battery.CATL;
import com.markus.desgin.mode.creational.factory.article.battery.Toshiba;/*** @author: markus* @date: 2024/3/16 11:25 PM* @Description:* @Blog: https://markuszhang.com* It's my honor to share what I've learned with you!*/
public class XiaomiCarFactory extends AbstractCarFactory {@Overrideprotected Car createCar(CarType carType) {BatteryFactory batteryFactory = new Toshiba();if (CarType.BASIC.equals(carType)) {return new XiaomiBasicCar(batteryFactory);} else if (CarType.FLAGSHIP.equals(carType)) {return new XiaomiFlagshipCar(batteryFactory);}throw new UnsupportedOperationException("不支持的汽车类型");}
}

比亚迪工厂

package com.markus.desgin.mode.creational.factory.article.abstractfactory;import com.markus.desgin.mode.creational.factory.article.battery.BatteryFactory;
import com.markus.desgin.mode.creational.factory.article.battery.CATL;
import com.markus.desgin.mode.creational.factory.article.battery.Toshiba;/*** @author: markus* @date: 2024/3/16 11:32 PM* @Description: 比亚迪汽车生产商* @Blog: https://markuszhang.com* It's my honor to share what I've learned with you!*/
public class BYDCarFactory extends AbstractCarFactory {@Overrideprotected Car createCar(CarType carType) {BatteryFactory batteryFactory = new CATL();Car car;if (CarType.BASIC.equals(carType)) {return new BYDBasicCar(batteryFactory);} else if (CarType.FLAGSHIP.equals(carType)) {return new BYDFlagshipCar(batteryFactory);}throw new UnsupportedOperationException("不支持的汽车类型");}
}
3.4 我们做了些什么?

相较于简单工厂,我们面对电池生产困难,汽车生产商将电池的生产交给具体的电池制造商这样一个问题,通过引入电池生产工厂 BatteryFactory来解决这一问题。利用这个接口书写代码,我们可以将电池的生产与实际的汽车生产解耦,以便在不同的汽车厂商(或者同一厂商下不同汽车类型中)应用不同的电池制造出不同的汽车。因为代码从实际的产品中解耦,所以我们可以替换不同的工厂来取得不同的行为。

上述模式就是抽象工厂模式,它提供一个接口,用于创建相关或依赖对象的家族而不需要明确指定具体类。我们来看下它的 UML 图(比较复杂)

image-20240317000129252

二、工厂模式在 Spring 中的应用

工厂模式在 Spring 中应用到很多地方,这里举两个例子:

  • BeanFactory,它就是一个巨大的生产 Bean 的工厂,客户端通过 BeanFactory#getBean 来获取相应的 Bean 实例,而无需关注 Bean 如何被创建的。
  • 如果大家看过 Spring AOP 源码的话,一定对 AopProxyFactory 感到熟悉,它的职责就是生产 AopProxy,而不同 AopProxy 又会创建具体的代理对象。
    • image-20240317000854726

三、设计模式百宝箱

在本节,我们开始填充我们的百宝箱:

  • 面向对象基础
    • 抽象
    • 封装
    • 多态
    • 继承
  • 面向对象原则
    • 依赖抽象,不要依赖具体类
    • 针对接口编程,不针对具体实现编程
    • 类应该对扩展开放,对修改关闭
    • 为交互对象之间的松耦合设计而努力
  • 面向对象设计模式
    • 简单工厂模式:定义了一个创建对象的接口,将创建对象的内容从客户端抽离出来
    • 抽象工厂模式:提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类

四、本文总结

好了,总结一下,在本篇的学习当中,我们可以总结出以下知识点:

  • 所有的工厂都是用来封装对象的创建。
  • 简单工厂,虽然不是真正的设计模式,但仍不失为一个简单的方法,可以将客户程序从具体类解耦。
  • 抽象工厂使用对象组合:对象的创建被实现在工厂接口所暴露出来的方法中。
  • 所有工厂模式都通过减少应用程序和具体类之间的依赖促进松耦合。
  • 抽象工厂创建相关的对象家族,而不需要依赖他们的具体类。
  • 依赖倒置原则,知道我们避免依赖具体类型,而要尽量依赖抽象。
  • 工厂是很有威力的技巧,帮助我们针对抽象编程,而不要针对具体类编程。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/2869193.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

Jenkins 面试题及答案整理,最新面试题

Jenkins中如何实现持续集成与持续部署? Jenkins通过自动化构建、测试和部署应用程序来实现持续集成与持续部署(CI/CD)。这个过程包括以下步骤: 1、源代码管理: Jenkins支持与多种版本控制系统集成,如Git、…

路由器端口转发远程桌面控制:一电脑连接不同局域网的另一电脑

一、引言 路由器端口转发:指在路由器上设置一定的规则,将外部的数据包转发到内部指定的设备或应用程序。这通常需要对路由器进行一些配置,以允许外部网络访问内部网络中的特定服务和设备。端口转发功能可以实现多种应用场景,例如远…

考研C语言复习进阶(6)

目录 1. 程序的翻译环境和执行环境 2. 详解编译链接 2.1 翻译环境 ​编辑​编辑 2.2 编译本身也分为几个阶段: 2.3 运行环境 3. 预处理详解 3.1 预定义符号 3.2 #define 3.2.1 #define 定义标识符 3.2.2 #define 定义宏 2.2.3 #define 替换规则 3.2.4…

Ubuntu 如何安装 Beyond Compare?

Ubuntu20.04安装Beyond Compare 4.3.7 一、官网下载方式一:方法二:使用 .deb 包安装 二、安装相关依赖和bcompare三、破解常见错误解决方法 ) 文件比较工具Beyond Compare是一套由Scooter Software推出的文件比较工具。主要用途是对比两个文件夹或者文件…

【大模型系列】问答理解定位(Qwen-VL/Llama2/GPT)

文章目录 1 Qwen-VL(2023, Alibaba)1.1 网络结构1.2 模型训练 2 Llama2(2023, Meta)2.1 网络结构2.1.1 MHA/GQA/MQA2.1.2 RoPE(Rotary Position Embedding, 旋转式位置编码)2.1.3 RMSNorm 2.2 推理2.2.1 集束搜索(beam search)2.2.2 RoPE外推 3 GPT系列(OpenAI) 1 Qwen-VL(2023…

贪心算法(两个实例)

例一:调度问题 问题:由n项任务,每项任务的加工时间已知,从零时刻开始陆续加入一台机器上去加工,每个任务完成的时间是从0时刻到任务加工截至的时间。 求总完成时间(所有任务完成时间最短计划方案&#xf…

vue3新功能-Teleport

1.teleport 在组件内的任何位置渲染内容 将一个组件内部的一部分模板“传送”到该组件的 DOM 结构外层的位置去。 例:将组件dialog添加到body下面 <teleport to"body"> <el- dialog --> </teleport> 2.fragments 多个根元素外层不需要…

解决Linux中Eclipse启动时找不到Java环境的问题

按照报错的意思是没有在/usr/local/eclipse/jre/bin/java下找到java环境&#xff0c;我检查了一下eclipse的目录结构发现在/usr/local/eclipse没有jre/bin/java&#xff0c;我的想法是自己建对应文件夹然后软连接到我的java环境 cd /usr/local/eclipse sudo mkdir jre cd jre s…

CSS其他属性

文章目录 1. vertical-align1.1. 概念1.2. 常用值1.3. 作用1.4. 出现的情况一1.4.1. 原因1.4.2. 解决方案 1.5. 出现情况二1.5.1. 解决方案一1.5.2. 解决方案二1.5.3. 解决方案三 1.6. 出现情况三1.6.1. 原因1.6.2. 解决方案 2. 溢出效果2.1. 作用2.2. 属性名 3. 隐藏效果3.1. …

软考78-上午题-【面向对象技术3-设计模式】-结构型设计模式01

一、适配器模式 1-1、意图 个类的接口转换成客户希望的另外一个接口。 Adapter 模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。 1-2、结构 适配器模式分为&#xff1a; 1、适配器类模式&#xff1b; 2、适配器对象模式 类适配器使用多重继承对一个接口与另…

Spring Cloud Alibaba微服务从入门到进阶(五)(负载均衡-Ribbon)

负载均衡有两种形式&#xff0c;服务器端负载均衡/客户端负载均衡 1、服务器端负载均衡 因为Nginx是部署在服务器端的&#xff0c;所以用Nginx实现的负载均衡被称为服务器端负载均衡 2、客户端负载均衡 手写一个客户端侧负载均衡器 使用Ribbon实现负载均衡 Ribbon是Netflix…

GitLab 面试题及答案整理,最新面试题

GitLab 在持续集成/持续部署(CI/CD)中的角色是什么&#xff1f; GitLab 在持续集成/持续部署(CI/CD)中扮演的角色非常关键&#xff0c;主要体现在以下几个方面&#xff1a; 1、自动化构建和测试&#xff1a; GitLab 可以自动化执行代码的构建和测试过程&#xff0c;确保代码提…

[自研开源] MyData 数据集成之数据过滤 v0.7.2

开源地址&#xff1a;gitee | github 详细介绍&#xff1a;MyData 基于 Web API 的数据集成平台 部署文档&#xff1a;用 Docker 部署 MyData 使用手册&#xff1a;MyData 使用手册 试用体验&#xff1a;https://demo.mydata.work 交流Q群&#xff1a;430089673 概述 本篇基于…

生态系统服务——食物生产功能分布数据

食物生产数据为县生态系统提供的粮食、水产品、肉类、林果产品等食物产量&#xff0c;统一转换为能量。 地理遥感生态网提供的生态系统服务——食物生产功能分布数据&#xff0c;计算中以县为单元对各种粮食、肉、蛋、奶、水果产量进行核算。其中&#xff0c;食物供给功…

实战:django项目环境搭建(pycharm,virtualBox)

django项目环境搭建 一.创建虚拟环境二.创建PyCharm远程连接 一.创建虚拟环境 需要用到的软件&#xff1a;PyCharm&#xff0c;VirtualBox虚拟机。 1.打开虚拟机终端&#xff0c;创建新的虚拟环境 Book。 2.在虚拟环境中创建新的文件夹 library&#xff0c;cd命令进入该文件…

《操作系统导论》第二章读书笔记

《操作系统导论》第二章读书笔记 —— 杭州 2024-03-17 夜 文章目录 《操作系统导论》第二章读书笔记1.操作系统&#xff08;Operating System&#xff0c;OS&#xff09;2.虚拟化CPU3.虚拟化内存4.并发5.持久性6.设计目标和简单历史 1.操作系统&#xff08;Operating System&a…

C#操作MySQL从入门到精通(4)——连接MySQL数据库

前言 我们创建好数据库、建立好数据库的表以后&#xff0c;我们就需要访问数据库了&#xff0c;比如将数据插入数据库的某张表中等一系列操作&#xff0c;在进行这些操作之前我们需要连接上数据库&#xff0c;本文就是详细讲解如何连接MySQL数据库的。 1、使用Navicat Premiu…

JavaWeb--HTML

一&#xff1a;HTML简介 *HTML是一门语言&#xff0c;所有的网页都是用HTML这门语言编写出来的&#xff1b; *HTML&#xff1a;超文本标记语言&#xff1b; 超文本&#xff1a;超越了文本的限制&#xff0c;比普通文本更强大。除了文字信息&#xff0c;还能定义图片&#xff…

给定参数c和长度为n的递增数组a(ai <= c), 对于0<=x<=y<=c, 求(x,y)的对数,满足x+y不是数组a中的元素且y-x不是a中元素

题目 思路&#xff1a; #include <bits/stdc.h> using namespace std; #define int long long #define pb push_back #define fi first #define se second #define lson p << 1 #define rson p << 1 | 1 const int maxn 1e6 5, inf 1e18, maxm 4e4 5, …

openssl3.2 - note - Writing OpenSSL Provider Skeleton

文章目录 openssl3.2 - note - Writing OpenSSL Provider Skeleton概述笔记测试工程的建立复现的provider工程总结Provider包含的头文件openssl/core.h中的数据结构实现 OSSL_provider_init()看一下openssl自带的提供者provider的openssl命令行测试provider的本质是hook了opens…