重读 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

相关文章

在Ubuntu上部署SpringBoot应用的操作步骤

《在Ubuntu上部署SpringBoot应用的操作步骤》随着云计算和容器化技术的普及,Linux服务器已成为部署Web应用程序的主流平台之一,Java作为一种跨平台的编程语言,具有广泛的应用场景,本... 目录一、部署准备二、安装 Java 环境1. 安装 JDK2. 验证 Java 安装三、安装 mys

Springboot的ThreadPoolTaskScheduler线程池轻松搞定15分钟不操作自动取消订单

《Springboot的ThreadPoolTaskScheduler线程池轻松搞定15分钟不操作自动取消订单》:本文主要介绍Springboot的ThreadPoolTaskScheduler线... 目录ThreadPoolTaskScheduler线程池实现15分钟不操作自动取消订单概要1,创建订单后

JAVA中整型数组、字符串数组、整型数和字符串 的创建与转换的方法

《JAVA中整型数组、字符串数组、整型数和字符串的创建与转换的方法》本文介绍了Java中字符串、字符数组和整型数组的创建方法,以及它们之间的转换方法,还详细讲解了字符串中的一些常用方法,如index... 目录一、字符串、字符数组和整型数组的创建1、字符串的创建方法1.1 通过引用字符数组来创建字符串1.2

SpringCloud集成AlloyDB的示例代码

《SpringCloud集成AlloyDB的示例代码》AlloyDB是GoogleCloud提供的一种高度可扩展、强性能的关系型数据库服务,它兼容PostgreSQL,并提供了更快的查询性能... 目录1.AlloyDBjavascript是什么?AlloyDB 的工作原理2.搭建测试环境3.代码工程1.

Java调用Python代码的几种方法小结

《Java调用Python代码的几种方法小结》Python语言有丰富的系统管理、数据处理、统计类软件包,因此从java应用中调用Python代码的需求很常见、实用,本文介绍几种方法从java调用Pyt... 目录引言Java core使用ProcessBuilder使用Java脚本引擎总结引言python

SpringBoot操作spark处理hdfs文件的操作方法

《SpringBoot操作spark处理hdfs文件的操作方法》本文介绍了如何使用SpringBoot操作Spark处理HDFS文件,包括导入依赖、配置Spark信息、编写Controller和Ser... 目录SpringBoot操作spark处理hdfs文件1、导入依赖2、配置spark信息3、cont

springboot整合 xxl-job及使用步骤

《springboot整合xxl-job及使用步骤》XXL-JOB是一个分布式任务调度平台,用于解决分布式系统中的任务调度和管理问题,文章详细介绍了XXL-JOB的架构,包括调度中心、执行器和Web... 目录一、xxl-job是什么二、使用步骤1. 下载并运行管理端代码2. 访问管理页面,确认是否启动成功

Java中的密码加密方式

《Java中的密码加密方式》文章介绍了Java中使用MD5算法对密码进行加密的方法,以及如何通过加盐和多重加密来提高密码的安全性,MD5是一种不可逆的哈希算法,适合用于存储密码,因为其输出的摘要长度固... 目录Java的密码加密方式密码加密一般的应用方式是总结Java的密码加密方式密码加密【这里采用的

Java中ArrayList的8种浅拷贝方式示例代码

《Java中ArrayList的8种浅拷贝方式示例代码》:本文主要介绍Java中ArrayList的8种浅拷贝方式的相关资料,讲解了Java中ArrayList的浅拷贝概念,并详细分享了八种实现浅... 目录引言什么是浅拷贝?ArrayList 浅拷贝的重要性方法一:使用构造函数方法二:使用 addAll(

解决mybatis-plus-boot-starter与mybatis-spring-boot-starter的错误问题

《解决mybatis-plus-boot-starter与mybatis-spring-boot-starter的错误问题》本文主要讲述了在使用MyBatis和MyBatis-Plus时遇到的绑定异常... 目录myBATis-plus-boot-starpythonter与mybatis-spring-b