重读 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实现检查多个时间段是否有重合

《Java实现检查多个时间段是否有重合》这篇文章主要为大家详细介绍了如何使用Java实现检查多个时间段是否有重合,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录流程概述步骤详解China编程步骤1:定义时间段类步骤2:添加时间段步骤3:检查时间段是否有重合步骤4:输出结果示例代码结语作

Java中String字符串使用避坑指南

《Java中String字符串使用避坑指南》Java中的String字符串是我们日常编程中用得最多的类之一,看似简单的String使用,却隐藏着不少“坑”,如果不注意,可能会导致性能问题、意外的错误容... 目录8个避坑点如下:1. 字符串的不可变性:每次修改都创建新对象2. 使用 == 比较字符串,陷阱满

Java判断多个时间段是否重合的方法小结

《Java判断多个时间段是否重合的方法小结》这篇文章主要为大家详细介绍了Java中判断多个时间段是否重合的方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录判断多个时间段是否有间隔判断时间段集合是否与某时间段重合判断多个时间段是否有间隔实体类内容public class D

IDEA编译报错“java: 常量字符串过长”的原因及解决方法

《IDEA编译报错“java:常量字符串过长”的原因及解决方法》今天在开发过程中,由于尝试将一个文件的Base64字符串设置为常量,结果导致IDEA编译的时候出现了如下报错java:常量字符串过长,... 目录一、问题描述二、问题原因2.1 理论角度2.2 源码角度三、解决方案解决方案①:StringBui

Java覆盖第三方jar包中的某一个类的实现方法

《Java覆盖第三方jar包中的某一个类的实现方法》在我们日常的开发中,经常需要使用第三方的jar包,有时候我们会发现第三方的jar包中的某一个类有问题,或者我们需要定制化修改其中的逻辑,那么应该如何... 目录一、需求描述二、示例描述三、操作步骤四、验证结果五、实现原理一、需求描述需求描述如下:需要在

Java中ArrayList和LinkedList有什么区别举例详解

《Java中ArrayList和LinkedList有什么区别举例详解》:本文主要介绍Java中ArrayList和LinkedList区别的相关资料,包括数据结构特性、核心操作性能、内存与GC影... 目录一、底层数据结构二、核心操作性能对比三、内存与 GC 影响四、扩容机制五、线程安全与并发方案六、工程

JavaScript中的reduce方法执行过程、使用场景及进阶用法

《JavaScript中的reduce方法执行过程、使用场景及进阶用法》:本文主要介绍JavaScript中的reduce方法执行过程、使用场景及进阶用法的相关资料,reduce是JavaScri... 目录1. 什么是reduce2. reduce语法2.1 语法2.2 参数说明3. reduce执行过程

如何使用Java实现请求deepseek

《如何使用Java实现请求deepseek》这篇文章主要为大家详细介绍了如何使用Java实现请求deepseek功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1.deepseek的api创建2.Java实现请求deepseek2.1 pom文件2.2 json转化文件2.2

Java调用DeepSeek API的最佳实践及详细代码示例

《Java调用DeepSeekAPI的最佳实践及详细代码示例》:本文主要介绍如何使用Java调用DeepSeekAPI,包括获取API密钥、添加HTTP客户端依赖、创建HTTP请求、处理响应、... 目录1. 获取API密钥2. 添加HTTP客户端依赖3. 创建HTTP请求4. 处理响应5. 错误处理6.

Spring AI集成DeepSeek的详细步骤

《SpringAI集成DeepSeek的详细步骤》DeepSeek作为一款卓越的国产AI模型,越来越多的公司考虑在自己的应用中集成,对于Java应用来说,我们可以借助SpringAI集成DeepSe... 目录DeepSeek 介绍Spring AI 是什么?1、环境准备2、构建项目2.1、pom依赖2.2