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

本文主要是介绍重读 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

三、设计模式百宝箱

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

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

四、本文总结

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

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

这篇关于重读 Java 设计模式: 深入探讨工厂模式,创建对象的灵活性与可维护性的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/818066

相关文章

Java学习手册之Filter和Listener使用方法

《Java学习手册之Filter和Listener使用方法》:本文主要介绍Java学习手册之Filter和Listener使用方法的相关资料,Filter是一种拦截器,可以在请求到达Servl... 目录一、Filter(过滤器)1. Filter 的工作原理2. Filter 的配置与使用二、Listen

Spring Boot中JSON数值溢出问题从报错到优雅解决办法

《SpringBoot中JSON数值溢出问题从报错到优雅解决办法》:本文主要介绍SpringBoot中JSON数值溢出问题从报错到优雅的解决办法,通过修改字段类型为Long、添加全局异常处理和... 目录一、问题背景:为什么我的接口突然报错了?二、为什么会发生这个错误?1. Java 数据类型的“容量”限制

Java对象转换的实现方式汇总

《Java对象转换的实现方式汇总》:本文主要介绍Java对象转换的多种实现方式,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录Java对象转换的多种实现方式1. 手动映射(Manual Mapping)2. Builder模式3. 工具类辅助映

SpringBoot请求参数接收控制指南分享

《SpringBoot请求参数接收控制指南分享》:本文主要介绍SpringBoot请求参数接收控制指南,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Spring Boot 请求参数接收控制指南1. 概述2. 有注解时参数接收方式对比3. 无注解时接收参数默认位置

SpringBoot基于配置实现短信服务策略的动态切换

《SpringBoot基于配置实现短信服务策略的动态切换》这篇文章主要为大家详细介绍了SpringBoot在接入多个短信服务商(如阿里云、腾讯云、华为云)后,如何根据配置或环境切换使用不同的服务商,需... 目录目标功能示例配置(application.yml)配置类绑定短信发送策略接口示例:阿里云 & 腾

SpringBoot项目中报错The field screenShot exceeds its maximum permitted size of 1048576 bytes.的问题及解决

《SpringBoot项目中报错ThefieldscreenShotexceedsitsmaximumpermittedsizeof1048576bytes.的问题及解决》这篇文章... 目录项目场景问题描述原因分析解决方案总结项目场景javascript提示:项目相关背景:项目场景:基于Spring

Spring Boot 整合 SSE的高级实践(Server-Sent Events)

《SpringBoot整合SSE的高级实践(Server-SentEvents)》SSE(Server-SentEvents)是一种基于HTTP协议的单向通信机制,允许服务器向浏览器持续发送实... 目录1、简述2、Spring Boot 中的SSE实现2.1 添加依赖2.2 实现后端接口2.3 配置超时时

Spring Boot读取配置文件的五种方式小结

《SpringBoot读取配置文件的五种方式小结》SpringBoot提供了灵活多样的方式来读取配置文件,这篇文章为大家介绍了5种常见的读取方式,文中的示例代码简洁易懂,大家可以根据自己的需要进... 目录1. 配置文件位置与加载顺序2. 读取配置文件的方式汇总方式一:使用 @Value 注解读取配置方式二

一文详解Java异常处理你都了解哪些知识

《一文详解Java异常处理你都了解哪些知识》:本文主要介绍Java异常处理的相关资料,包括异常的分类、捕获和处理异常的语法、常见的异常类型以及自定义异常的实现,文中通过代码介绍的非常详细,需要的朋... 目录前言一、什么是异常二、异常的分类2.1 受检异常2.2 非受检异常三、异常处理的语法3.1 try-

Java中的@SneakyThrows注解用法详解

《Java中的@SneakyThrows注解用法详解》:本文主要介绍Java中的@SneakyThrows注解用法的相关资料,Lombok的@SneakyThrows注解简化了Java方法中的异常... 目录前言一、@SneakyThrows 简介1.1 什么是 Lombok?二、@SneakyThrows