【设计模式】【创建型模式】工厂方法模式(Factory Methods)

简介: 一、入门 什么是工厂方法模式? 工厂方法模式(Factory Method Pattern)是一种创建型设计模式,它定义了一个用于创建对象的接口,但由子类决定实例化哪个类。工厂方法模式使类的实例化延迟

👋hi,我不是一名外包公司的员工,也不会偷吃茶水间的零食,我的梦想是能写高端CRUD

🔥 2025本人正在沉淀中... 博客更新速度++

👍 欢迎点赞、收藏、关注,跟上我的更新节奏

🎵 当你的天空突然下了大雨,那是我在为你炸乌云

一、入门

什么是工厂方法模式?

工厂方法模式(Factory Method Pattern)是一种创建型设计模式,它定义了一个用于创建对象的接口,但由子类决定实例化哪个类。工厂方法模式使类的实例化延迟到子类。
简单工厂方法就是所有的产品都交给一个工厂来处理,而工厂方法是交给多个工厂。

为什么要工厂方法模式?

假设我们正在开发一个支付系统,支持多种支付方式(如支付宝、微信支付、银行卡支付等)。在没有工厂方法模式的情况下,代码可能是这样的:

// 支付接口
interface Payment {
   
    void pay(double amount);
}

// 支付宝支付
class Alipay implements Payment {
   
    @Override
    public void pay(double amount) {
   
        System.out.println("使用支付宝支付: " + amount + " 元");
    }
}

// 微信支付
class WechatPay implements Payment {
   
    @Override
    public void pay(double amount) {
   
        System.out.println("使用微信支付: " + amount + " 元");
    }
}

// 银行卡支付
class BankCardPay implements Payment {
   
    @Override
    public void pay(double amount) {
   
        System.out.println("使用银行卡支付: " + amount + " 元");
    }
}

// 客户端代码
public class PaymentSystem {
   
    public static void main(String[] args) {
   
        String paymentType = "alipay"; // 假设从配置或用户输入中获取支付方式
        Payment payment = null;

        if (paymentType.equals("alipay")) {
   
            payment = new Alipay();
        } else if (paymentType.equals("wechatpay")) {
   
            payment = new WechatPay();
        } else if (paymentType.equals("bankcard")) {
   
            payment = new BankCardPay();
        } else {
   
            throw new IllegalArgumentException("不支持的支付方式");
        }

        payment.pay(100.0); // 执行支付
    }
}

问题分析

  1. 代码耦合性高
    • 客户端代码(PaymentSystem)直接依赖具体的支付实现类(如 Alipay、WechatPay 等)。
    • 如果需要新增一种支付方式(如 ApplePay),必须修改客户端代码,增加新的 if-else 分支。
  2. 违反开闭原则
    • 开闭原则(Open/Closed Principle)要求软件实体应对扩展开放,对修改关闭。
    • 在当前实现中,每次新增支付方式都需要修改客户端代码,而不是通过扩展来实现。
  3. 代码重复
    • 如果在多个地方需要创建支付对象,相同的 if-else 逻辑会重复出现,导致代码冗余。
  4. 难以测试
    • 客户端代码与具体支付实现类耦合,难以进行单元测试(例如,无法轻松模拟支付对象)。

如何实现工厂方法模式

主要角色

  1. 抽象产品(Product):定义产品的接口。
  2. 具体产品(Concrete Product):实现抽象产品接口的具体类。
  3. 抽象工厂(Creator):声明工厂方法,返回一个产品对象。
  4. 具体工厂(Concrete Creator):实现工厂方法,返回具体产品的实例。

【案例】支付方式 - 改

image.png

抽象产品(Product):支付方式,Payment类。

// 支付接口
interface Payment {
   
    void pay(double amount);
}

具体产品(Concrete Product): 支付宝支付(Alipay类)、微信支付(WechatPay类)、银行卡支付(BankCardPay类)。

// 支付宝支付
class Alipay implements Payment {
   
    @Override
    public void pay(double amount) {
   
        System.out.println("使用支付宝支付: " + amount + " 元");
    }
}

// 微信支付
class WechatPay implements Payment {
   
    @Override
    public void pay(double amount) {
   
        System.out.println("使用微信支付: " + amount + " 元");
    }
}

// 银行卡支付
class BankCardPay implements Payment {
   
    @Override
    public void pay(double amount) {
   
        System.out.println("使用银行卡支付: " + amount + " 元");
    }
}

抽象工厂(Creator): 支付工厂,PaymentFactory类。

interface PaymentFactory {
   
    Payment createPayment();
}

具体工厂(Concrete Creator)AlipayFactoryWechatPayFactoryBankCardPayFactory

// 支付宝支付工厂
class AlipayFactory implements PaymentFactory {
   
    @Override
    public Payment createPayment() {
   
        return new Alipay();
    }
}

// 微信支付工厂
class WechatPayFactory implements PaymentFactory {
   
    @Override
    public Payment createPayment() {
   
        return new WechatPay();
    }
}

// 银行卡支付工厂
class BankCardPayFactory implements PaymentFactory {
   
    @Override
    public Payment createPayment() {
   
        return new BankCardPay();
    }
}

客户端调用

public class PaymentSystem {
   
    // 支付方式与工厂类的映射
    private static final Map<String, PaymentFactory> paymentFactories = new HashMap<>();

    static {
   
        // 初始化映射关系
        paymentFactories.put("alipay", new AlipayFactory());
        paymentFactories.put("wechatpay", new WechatPayFactory());
        paymentFactories.put("bankcard", new BankCardPayFactory());
    }

    // 获取支付对象
    public static Payment getPayment(String paymentType) {
   
        PaymentFactory factory = paymentFactories.get(paymentType);
        if (factory == null) {
   
            throw new IllegalArgumentException("不支持的支付方式: " + paymentType);
        }
        return factory.createPayment();
    }

    public static void main(String[] args) {
   
        String paymentType = "alipay"; // 假设从配置或用户输入中获取支付方式

        // 获取支付对象并执行支付
        Payment payment = getPayment(paymentType);
        payment.pay(100.0);
    }
}

二、工厂方法模式在框架源码中的运用

Java 集合框架中的工厂方法模式

Java 集合框架中的 Collections 工具类提供了多个静态工厂方法,用于创建不可变集合、同步集合等。
示例:Collections.unmodifiableList()
Collections.unmodifiableList() 是一个工厂方法,用于创建不可修改的列表。

public class Collections {
   
    public static <T> List<T> unmodifiableList(List<? extends T> list) {
   
        return new UnmodifiableList<>(list);
    }

    private static class UnmodifiableList<E> extends UnmodifiableCollection<E>
            implements List<E> {
   
        // 具体实现...
    }
}
  • 工厂方法:unmodifiableList() 是工厂方法,负责创建 UnmodifiableList 对象。
  • 客户端:客户端通过调用 unmodifiableList() 方法获取不可修改的列表,而无需关心具体的实现类。
List<String> originalList = new ArrayList<>();
originalList.add("A");
originalList.add("B");

List<String> unmodifiableList = Collections.unmodifiableList(originalList);
// unmodifiableList.add("C"); // 抛出 UnsupportedOperationException
  • 解耦:客户端不需要知道 UnmodifiableList 的具体实现,只需要通过工厂方法获取对象。
  • 安全性:工厂方法返回的对象是不可修改的,保证了数据的安全性。

Spring 框架中的工厂方法模式

Spring 框架广泛使用工厂方法模式来创建和管理 Bean 对象。Spring 的 BeanFactoryApplicationContext 是典型的工厂方法模式的实现。
示例:BeanFactory
BeanFactory 是 Spring 的核心接口,负责创建和管理 Bean 对象。它定义了工厂方法 getBean(),用于获取 Bean 实例。

public interface BeanFactory {
   
    Object getBean(String name) throws BeansException;
    <T> T getBean(String name, Class<T> requiredType) throws BeansException;
    // 其他方法...
}
  • 工厂方法getBean() 是工厂方法,负责根据名称或类型创建并返回 Bean 对象。
  • 具体工厂:Spring 提供了多个 BeanFactory 的实现类,例如 DefaultListableBeanFactory
  • 客户端:Spring 容器(如 ApplicationContext)通过调用 getBean() 方法来获取 Bean 对象。
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
MyService myService = context.getBean("myService", MyService.class);
myService.doSomething();
  • 解耦:客户端代码(如 MyService)不需要知道具体的 Bean 创建逻辑,只需要通过 getBean() 方法获取对象。
  • 灵活性:Spring 可以通过配置文件或注解动态决定创建哪个 Bean 对象。

三、总结

工厂方法模式的优点

  1. 解耦
    • 工厂方法模式将对象的创建与使用分离,客户端代码只需要依赖抽象的工厂接口和产品接口,而不需要关心具体的实现类。
    • 例如,在 Spring 框架中,客户端通过 BeanFactory 获取 Bean 对象,而不需要知道具体的 Bean 实现类。
  2. 符合开闭原则
    • 当需要新增一种产品时,只需新增一个具体工厂类和具体产品类,而无需修改现有代码。
    • 例如,在支付系统中,新增一种支付方式(如 ApplePay)时,只需新增 ApplePayFactoryApplePay 类,而不需要修改客户端代码。
  3. 可扩展性
    • 工厂方法模式支持动态扩展,可以通过配置文件、依赖注入等方式动态决定使用哪个具体工厂类。
    • 例如,在 JDBC 中,可以通过配置文件动态切换数据库驱动。
  4. 代码复用
    • 工厂类的逻辑可以复用在多个地方,避免了重复的创建逻辑。
    • 例如,在 Log4j 中,Logger.getLogger() 方法可以在多个地方复用,而不需要重复编写日志记录器的创建逻辑。
  5. 易于测试
    • 客户端代码依赖于抽象的工厂接口和产品接口,可以通过模拟工厂类来测试客户端代码,而不需要依赖具体的实现类。

工厂方法模式的缺点

  1. 类的数量增加
    • 每新增一种产品,就需要新增一个具体工厂类和具体产品类,导致类的数量增加,系统复杂度提高。
    • 例如,在支付系统中,如果有 10 种支付方式,就需要 10 个具体工厂类和 10 个具体产品类。
  2. 增加了系统抽象性
    • 工厂方法模式引入了抽象的工厂接口和产品接口,增加了系统的抽象性,可能会使代码更难理解。
    • 例如,对于初学者来说,理解 Spring 的 BeanFactoryApplicationContext 可能需要一定的学习成本。
  3. 不适合简单场景
    • 如果对象的创建逻辑非常简单,使用工厂方法模式可能会显得过于复杂,增加不必要的代码量。
    • 例如,如果只需要创建一个简单的对象,直接使用 new 关键字可能更合适。

工厂方法模式的适用场景

  1. 需要动态创建对象
    • 当对象的创建逻辑需要根据运行时条件动态决定时,工厂方法模式非常适用。
    • 例如,在支付系统中,根据用户选择的支付方式动态创建支付对象。
  2. 需要解耦对象的创建与使用
    • 当客户端代码不需要关心具体对象的创建逻辑时,可以使用工厂方法模式将对象的创建过程封装起来。
    • 例如,在 Spring 框架中,客户端通过 BeanFactory 获取 Bean 对象,而不需要知道具体的 Bean 实现类。
  3. 需要支持多种产品类型
    • 当系统需要支持多种产品类型,并且这些产品有共同的接口时,可以使用工厂方法模式。
    • 例如,在日志框架中,支持多种日志实现(如文件日志、控制台日志等)。
  4. 需要符合开闭原则
    • 当系统需要频繁扩展新的产品类型,并且希望在不修改现有代码的情况下实现扩展时,可以使用工厂方法模式。
    • 例如,在 GUI 库中,支持新增一种控件类型(如按钮、文本框等)。
  5. 需要集中管理对象的创建逻辑
    • 当对象的创建逻辑比较复杂,或者需要在多个地方复用时,可以使用工厂方法模式将创建逻辑集中管理。
    • 例如,在 JDBC 中,通过 DriverManager.getConnection() 集中管理数据库连接的创建逻辑。
目录
相关文章
|
11天前
|
设计模式 缓存 安全
【设计模式】【创建型模式】单例模式(Singleton)
一、入门 什么是单例模式? 单例模式是一种设计模式,确保一个类只有一个实例,并提供一个全局访问点。它常用于需要全局唯一对象的场景,如配置管理、连接池等。 为什么要单例模式? 节省资源 场景:某些对象创
69 15
|
11天前
|
设计模式 JavaScript Java
【设计模式】【创建型模式】原型模式(Prototype)
一、入门 什么是原型模式? 原型模式(Prototype Pattern)是一种创建型设计模式,它通过复制现有对象来创建新对象,而不是通过实例化类。 原型模式的核心是克隆(Clone),即通过复制现有
47 15
|
11天前
|
设计模式 Java 关系型数据库
【设计模式】【创建型模式】抽象工厂模式(Abstract Factory)
一、入门 什么是抽象工厂模式? 抽象工厂模式是一种创建型设计模式,它提供了一个接口,用于创建相关或依赖对象的家族,而不需要指定具体的类。 简单来说,抽象工厂模式是工厂方法模式的升级版,它能够创建一组相
59 14
|
5月前
|
设计模式 前端开发 搜索推荐
前端必须掌握的设计模式——模板模式
模板模式(Template Pattern)是一种行为型设计模式,父类定义固定流程和步骤顺序,子类通过继承并重写特定方法实现具体步骤。适用于具有固定结构或流程的场景,如组装汽车、包装礼物等。举例来说,公司年会节目征集时,蜘蛛侠定义了歌曲的四个步骤:前奏、主歌、副歌、结尾。金刚狼和绿巨人根据此模板设计各自的表演内容。通过抽象类定义通用逻辑,子类实现个性化行为,从而减少重复代码。模板模式还支持钩子方法,允许跳过某些步骤,增加灵活性。
267 11
|
6月前
|
设计模式 安全 Java
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
|
2月前
|
设计模式 Java 关系型数据库
设计模式:工厂方法模式(Factory Method)
工厂方法模式是一种创建型设计模式,通过将对象的创建延迟到子类实现解耦。其核心是抽象工厂声明工厂方法返回抽象产品,具体工厂重写该方法返回具体产品实例。适用于动态扩展产品类型、复杂创建逻辑和框架设计等场景,如日志记录器、数据库连接池等。优点包括符合开闭原则、解耦客户端与具体产品;缺点是可能增加类数量和复杂度。典型应用如Java集合框架、Spring BeanFactory等。
|
4月前
|
设计模式
「全网最细 + 实战源码案例」设计模式——模式扩展(配置工厂)
该设计通过配置文件和反射机制动态选择具体工厂,减少硬编码依赖,提升系统灵活性和扩展性。配置文件解耦、反射创建对象,新增产品族无需修改客户端代码。示例中,`CoffeeFactory`类加载配置文件并使用反射生成咖啡对象,客户端调用时只需指定名称即可获取对应产品实例。
108 40
|
8月前
|
设计模式 数据库连接 PHP
PHP中的设计模式:提升代码的可维护性与扩展性在软件开发过程中,设计模式是开发者们经常用到的工具之一。它们提供了经过验证的解决方案,可以帮助我们解决常见的软件设计问题。本文将介绍PHP中常用的设计模式,以及如何利用这些模式来提高代码的可维护性和扩展性。我们将从基础的设计模式入手,逐步深入到更复杂的应用场景。通过实际案例分析,读者可以更好地理解如何在PHP开发中应用这些设计模式,从而写出更加高效、灵活和易于维护的代码。
本文探讨了PHP中常用的设计模式及其在实际项目中的应用。内容涵盖设计模式的基本概念、分类和具体使用场景,重点介绍了单例模式、工厂模式和观察者模式等常见模式。通过具体的代码示例,展示了如何在PHP项目中有效利用设计模式来提升代码的可维护性和扩展性。文章还讨论了设计模式的选择原则和注意事项,帮助开发者在不同情境下做出最佳决策。
|
4月前
|
设计模式 关系型数据库
「全网最细 + 实战源码案例」设计模式——简单工厂模式
简单工厂模式是一种创建型设计模式,通过工厂类根据传入参数创建不同类型的对象,也称“静态工厂方法”模式。其结构包括工厂类、产品接口和具体产品类。优点是封装性强、代码复用性好;缺点是扩展性差,增加新产品时需修改工厂类代码,违反开闭原则。适用于对象种类较少且调用者无需关心创建细节的场景。
104 19
|
4月前
|
设计模式 Java
「全网最细 + 实战源码案例」设计模式——生成器模式
生成器模式(Builder Pattern)是一种创建型设计模式,用于分步骤构建复杂对象。它允许用户通过控制对象构造的过程,定制对象的组成部分,而无需直接实例化细节。该模式特别适合构建具有多种配置的复杂对象。其结构包括抽象建造者、具体建造者、指挥者和产品角色。适用于需要创建复杂对象且对象由多个部分组成、构造过程需对外隐藏或分离表示与构造的场景。优点在于更好的控制、代码复用和解耦性;缺点是增加复杂性和不适合简单对象。实现时需定义建造者接口、具体建造者类、指挥者类及产品类。链式调用是常见应用方式之一。
88 12

热门文章

最新文章

OSZAR »