工厂方法模式(Factory Method)

工厂模式 ------ 提升代码扩展性的利器

今天我们来学习设计模式中应用最广泛、最能体现面向对象设计原则 的模式之一------工厂模式

工厂模式,也称为工厂方法模式,是一种创建型设计模式 。它在父类中提供一个创建对象的方法,允许子类决定实例化对象的类型。

这种设计模式也是Java开发中最常见的一种模式,它的主要意图是:定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。

简单说,工厂模式就是为了提供代码结构的扩展性 ,屏蔽每一个功能类中的具体实现逻辑,让外部可以更加简单地只知道调用即可。同时,它也可以帮助我们去掉众多 if-else 带来的复杂逻辑。当然,它也有一些缺点,比如需要实现的类会变多,维护成本增加。但这些问题都可以通过后续的设计模式组合来逐步降低。

接下来,我会带着大家从真实问题出发,一步步分析工厂模式的价值、实现方式、优缺点以及与其他模式的关联。请大家带着下面几个问题边看边思考,最好把案例代码自己敲一遍:

  • 为什么使用工厂模式?
  • 工厂模式如何实现?
  • 工厂模式有哪些优缺点?
  • 工厂模式与简单工厂、抽象工厂有什么区别?
  • 在Spring等框架中,工厂模式是如何体现的?

一、为什么使用工厂模式?------一个真实案例带你理解

我们先看一个常见场景:假设我们正在开发一个支付系统 ,支持多种支付方式:支付宝、微信支付、银行卡支付等。

在没有使用工厂模式的情况下,我们可能会写出这样的代码:

复制代码
public class PaymentService {
    public void pay(String type, double amount) {
        if ("alipay".equals(type)) {
            Alipay alipay = new Alipay();
            alipay.pay(amount);
        } else if ("wechat".equals(type)) {
            WechatPay wechat = new WechatPay();
            wechat.pay(amount);
        } else if ("bankcard".equals(type)) {
            BankCardPay bankCard = new BankCardPay();
            bankCard.pay(amount);
        } else {
            throw new UnsupportedOperationException("不支持的支付类型");
        }
    }
}

这段代码有什么问题?

  1. 违反开闭原则 :每当增加一种新的支付方式,我们都得修改 PaymentService 中的 if-else 逻辑,每次修改都可能引入bug。
  2. 代码臃肿 :随着支付方式增多,if-else 会无限膨胀,可读性和维护性急剧下降。
  3. 耦合度高PaymentService 直接依赖了具体的支付类(AlipayWechatPay),如果要替换实现或增加新功能,改动成本高。

工厂模式如何解决?

我们将创建支付对象的过程从 PaymentService 中抽离出来,交给一个专门的工厂 去管理。PaymentService 只需要知道工厂,不再依赖具体的支付类。这样一来,新增支付方式时,只需要新增一个具体的支付类和一个对应的工厂类(或修改工厂逻辑),PaymentService 完全不需要改动。

下面我们用工厂方法模式来重构这段代码。


二、工厂模式如何实现?------三种典型形式

工厂模式主要分为三种:简单工厂工厂方法抽象工厂。我们先从最简单的讲起,逐步深入。

1. 简单工厂(非GoF设计模式,但常用)

简单工厂并不属于23种GoF设计模式,但它是工厂模式的基础,很多项目都在用。它的核心是一个工厂类根据传入的参数返回不同的产品实例

复制代码
// 抽象出产品接口
public interface Payment {
    void pay(double amount);
}

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

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

// 简单工厂
public class PaymentFactory {
    public static Payment createPayment(String type) {
        if ("alipay".equals(type)) {
            return new Alipay();
        } else if ("wechat".equals(type)) {
            return new WechatPay();
        } else {
            throw new IllegalArgumentException("不支持的支付类型:" + type);
        }
    }
}

// 使用
public class PaymentService {
    public void pay(String type, double amount) {
        Payment payment = PaymentFactory.createPayment(type);
        payment.pay(amount);
    }
}

优点

  • 将对象的创建和使用分离,客户端不需要关心具体创建细节。
  • 减少了客户端代码的重复(不用在每个地方都写 newif-else)。

缺点

  • 工厂类负责所有产品的创建,一旦新增产品,工厂类就需要修改,违反了开闭原则。
  • 产品较多时,工厂类会变得臃肿。

2. 工厂方法模式(GoF设计模式)

工厂方法模式将工厂也抽象化:定义一个创建对象的抽象方法,由子类决定实例化哪个具体产品。这样每个具体产品都有对应的具体工厂,新增产品时只需要新增具体工厂,无需修改已有代码。

复制代码
// 抽象工厂
public abstract class PaymentFactory {
    // 工厂方法,延迟到子类实现
    public abstract Payment createPayment();
    
    // 业务方法(可选)
    public void pay(double amount) {
        Payment payment = createPayment();
        payment.pay(amount);
    }
}

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

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

// 使用
public class PaymentService {
    public void pay(PaymentFactory factory, double amount) {
        factory.pay(amount);
    }
}

// 客户端
PaymentService service = new PaymentService();
service.pay(new AlipayFactory(), 100.0);
service.pay(new WechatPayFactory(), 200.0);

优点

  • 符合开闭原则:新增产品时,只需新增对应的具体工厂类,不需要修改已有代码。
  • 单一职责:每个具体工厂只负责创建一种产品。
  • 支持扩展:可以在工厂子类中增加额外的初始化逻辑。

缺点

  • 类的数量增加:每增加一个产品,就需要增加一个具体工厂类,导致系统复杂度上升。
  • 如果产品类型非常多,代码量会很大。

3. 抽象工厂模式(GoF设计模式)

抽象工厂模式用于创建一组相关或相互依赖的对象,而不是单个对象。比如支付系统不仅需要支付对象,还需要退款对象、对账对象等。抽象工厂会提供一个接口,用于创建这一系列对象。

复制代码
// 抽象产品A:支付
public interface Payment { ... }

// 抽象产品B:退款
public interface Refund { ... }

// 具体产品族:支付宝系列
public class AlipayPayment implements Payment { ... }
public class AlipayRefund implements Refund { ... }

// 具体产品族:微信系列
public class WechatPayment implements Payment { ... }
public class WechatRefund implements Refund { ... }

// 抽象工厂
public interface PaymentFactory {
    Payment createPayment();
    Refund createRefund();
}

// 具体工厂:支付宝工厂
public class AlipayFactory implements PaymentFactory {
    @Override
    public Payment createPayment() {
        return new AlipayPayment();
    }
    @Override
    public Refund createRefund() {
        return new AlipayRefund();
    }
}

// 具体工厂:微信工厂
public class WechatFactory implements PaymentFactory {
    @Override
    public Payment createPayment() {
        return new WechatPayment();
    }
    @Override
    public Refund createRefund() {
        return new WechatRefund();
    }
}

优点

  • 保证同一产品族的产品一起使用,一致性高。
  • 易于替换产品族(只需切换具体工厂)。
  • 符合开闭原则,增加新的产品族时不需要修改已有代码。

缺点

  • 增加新产品类型(比如增加"对账"产品)时,需要修改所有工厂接口和具体工厂,扩展性较差。

三、工厂模式有哪些优缺点?

优点

  1. 解耦:将对象的创建和使用分离,客户端无需知道具体类的名称,只需知道工厂。
  2. 扩展性好:新增产品时,只需增加对应的工厂类,符合开闭原则(工厂方法模式)。
  3. 代码复用 :工厂中的创建逻辑可以复用,避免在多个地方重复 new
  4. 符合单一职责原则:将创建对象的职责集中到工厂,让客户端专注于业务逻辑。

缺点

  1. 类数量增加:每增加一个产品,就需增加一个具体工厂类,系统复杂度上升。
  2. 理解成本高:对于简单场景,引入工厂模式可能会过度设计,增加学习成本。
  3. 抽象工厂扩展困难:在抽象工厂中增加新产品类型时,需要修改所有工厂类,违反开闭原则。

四、工厂模式与简单工厂、抽象工厂的区别

|----------|----------------------|---------------|-------------------|
| 模式 | 定义 | 适用场景 | 优缺点 |
| 简单工厂 | 一个工厂类根据参数返回不同产品实例 | 产品种类较少,且不频繁增加 | 简单,但违反开闭原则 |
| 工厂方法 | 将工厂抽象,由子类决定实例化哪个具体产品 | 产品种类多,且可能频繁增加 | 符合开闭原则,但类数量多 |
| 抽象工厂 | 创建一组相关产品对象 | 需要保证产品族一致性 | 易于替换产品族,但增加产品类型困难 |


五、在Spring等框架中,工厂模式是如何体现的?

Spring框架大量使用了工厂模式,尤其是BeanFactoryApplicationContext,它们就是典型的工厂模式应用。

  • BeanFactory:是Spring容器的根接口,定义获取Bean的方法。它根据配置(XML或注解)创建和管理Bean实例,这就是工厂模式的思想。
  • ApplicationContext:继承自BeanFactory,提供了更多企业级功能,本质上也是一个工厂。
  • FactoryBean接口:允许自定义复杂对象的创建逻辑,是工厂方法模式的体现。

在Spring中,我们通常只需要通过注解或XML定义Bean,容器负责创建和管理,实现了对象创建和使用的完全解耦。


六、工厂模式与依赖注入(DI)的关系

依赖注入是控制反转的一种实现方式,它和工厂模式有异曲同工之妙:都是将对象的创建权从使用方移交给第三方。

  • 工厂模式是编程层面的创建者,由开发者编写工厂类决定创建逻辑。
  • 依赖注入是容器层面的创建者,由框架(如Spring)负责创建和管理对象,开发者只需声明依赖。

在现代Java开发中,我们更倾向于使用依赖注入(如Spring)来替代手写的工厂模式,因为框架提供了更强大、更灵活的管理能力(如作用域、生命周期回调、AOP等)。但理解工厂模式对于理解Spring原理至关重要。


七、总结

今天我们通过支付系统的案例,看到了工厂模式如何帮助我们去掉 if-else、提升代码扩展性。同时,我们也分析了工厂模式的优缺点,以及与Spring框架的关联。

关键点回顾:

  • 工厂模式的核心是将对象的创建和使用分离。
  • 工厂方法模式符合开闭原则,但会增加类数量。
  • 抽象工厂模式适合创建一组相关对象。
  • 在Spring中,BeanFactory和FactoryBean都是工厂模式的体现。
相关推荐
扶苏-su2 小时前
Java反射实战:动态操作Car类属性
java
Alanzeeb2 小时前
博客系统测试文档
java·javascript·功能测试·可用性测试
AY呀2 小时前
# 从手写 debounce 到企业级实现:我在面试中如何“降维打击”面试官
前端·面试
anzhxu2 小时前
MySQL Workbench菜单汉化为中文
java
96772 小时前
C++ 内存管理的核心——RAII 机制。两种锁 lock_guard, unique_lock
java·jvm·c++
bearpping2 小时前
MySQL JSON数据类型全解析(JSON datatype and functions)
java
lclcooky2 小时前
JavaWeb项目打包、部署至Tomcat并启动的全程指南(图文详解)
java·tomcat
想进大厂的小徐2 小时前
Spring 容器启动与 Bean 创建流程
java·spring boot·spring
dreamxian2 小时前
微服务1 -- MybatisPlus
java·微服务·架构