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

引言

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

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

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

一、新能源汽车的发展

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

1、无模式状况下

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

java 复制代码
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、简单工厂模式

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

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

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

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

java 复制代码
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 {

    @Override
    public Car createCar(CarType carType) {
        if (carType.equals(CarType.XIAOMI)) {
            return new XiaoMiCar();
        } else if (carType.equals(CarType.BYD)) {
            return new BYDCar();
        }
        throw new UnsupportedOperationException("其他汽车类型不支持创建!");
    }
}

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

java 复制代码
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 图来展示下工厂方法的应用:

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

3、抽象工厂模式

3.1 汽车

接口定义

java 复制代码
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();
}

小米基础版汽车

java 复制代码
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);
    }

    @Override
    public void installWheel() {
        System.out.println("安装小米基础版轮子");
    }

    @Override
    public void installSeat() {
        System.out.println("安装小米基础版座椅");
    }
}

小米旗舰版汽车

java 复制代码
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);
    }

    @Override
    public void installWheel() {
        System.out.println("安装小米旗舰版轮子");
    }

    @Override
    public void installSeat() {
        System.out.println("安装小米旗舰版座椅");
    }
}

比亚迪基础版汽车

java 复制代码
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);
    }

    @Override
    public void installWheel() {
        System.out.println("安装比亚迪基础版轮子");
    }

    @Override
    public void installSeat() {
        System.out.println("安装比亚迪基础版座椅");
    }
}

比亚迪旗舰版汽车

java 复制代码
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);
    }

    @Override
    public void installWheel() {
        System.out.println("安装比亚迪旗舰版轮子");
    }

    @Override
    public void installSeat() {
        System.out.println("安装比亚迪旗舰版座椅");
    }
}

3.2 电池制造厂定义

java 复制代码
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();
}

宁德时代

java 复制代码
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 {
    @Override
    public String productBattery() {
        return "宁德时代电池";
    }
}

东芝

java 复制代码
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 {
    @Override
    public String productBattery() {
        return "东芝电池";
    }
}

3.3 汽车制造厂定义

小米工厂

java 复制代码
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 {

    @Override
    protected 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("不支持的汽车类型");
    }
}

比亚迪工厂

java 复制代码
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 {

    @Override
    protected 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 图(比较复杂)

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

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

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

三、设计模式百宝箱

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

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

四、本文总结

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

  • 所有的工厂都是用来封装对象的创建。
  • 简单工厂,虽然不是真正的设计模式,但仍不失为一个简单的方法,可以将客户程序从具体类解耦。
  • 抽象工厂使用对象组合:对象的创建被实现在工厂接口所暴露出来的方法中。
  • 所有工厂模式都通过减少应用程序和具体类之间的依赖促进松耦合。
  • 抽象工厂创建相关的对象家族,而不需要依赖他们的具体类。
  • 依赖倒置原则,知道我们避免依赖具体类型,而要尽量依赖抽象。
  • 工厂是很有威力的技巧,帮助我们针对抽象编程,而不要针对具体类编程。
相关推荐
腥臭腐朽的日子熠熠生辉24 分钟前
解决maven失效问题(现象:maven中只有jdk的工具包,没有springboot的包)
java·spring boot·maven
ejinxian26 分钟前
Spring AI Alibaba 快速开发生成式 Java AI 应用
java·人工智能·spring
杉之31 分钟前
SpringBlade 数据库字段的自动填充
java·笔记·学习·spring·tomcat
圈圈编码1 小时前
Spring Task 定时任务
java·前端·spring
俏布斯1 小时前
算法日常记录
java·算法·leetcode
27669582921 小时前
美团民宿 mtgsig 小程序 mtgsig1.2 分析
java·python·小程序·美团·mtgsig·mtgsig1.2·美团民宿
爱的叹息1 小时前
Java 连接 Redis 的驱动(Jedis、Lettuce、Redisson、Spring Data Redis)分类及对比
java·redis·spring
程序猿chen1 小时前
《JVM考古现场(十五):熵火燎原——从量子递归到热寂晶壁的代码涅槃》
java·jvm·git·后端·java-ee·区块链·量子计算
松韬2 小时前
Spring + Redisson:从 0 到 1 搭建高可用分布式缓存系统
java·redis·分布式·spring·缓存
绝顶少年2 小时前
Spring Boot 注解:深度解析与应用场景
java·spring boot·后端