35_Java设计模式之工厂模式

Java设计模式之工厂模式

文章目录

前言

工厂模式(Factory Pattern)是创建型设计模式中应用最广泛的模式家族,它封装了对象的创建逻辑,让调用方无需关心具体的实现细节。工厂模式分为三种演进形态:简单工厂工厂方法抽象工厂。本文将通过一个"支付系统"的案例,逐步揭示它们的区别与适用场景。

学习建议:工厂模式的三种形态不是孤立存在的,而是一个随着需求复杂度增长而自然演进的过程。从一个小项目中"一个switch解决问题"的简单工厂,到产品种类增多后"为每个产品配一个工厂"的工厂方法,再到多产品族场景下"创建成套产品"的抽象工厂------这条演进脉络比记忆三种模式的类图更有价值。面试中常见的问题是让你比较这三种模式,并举例说明各自的使用场景。

一、简单工厂模式

简单工厂通过一个工厂类根据参数决定创建哪种产品实例:

java 复制代码
// 产品接口
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 BankPay implements Payment {
    @Override
    public void pay(double amount) {
        System.out.println("银行卡支付: ¥" + amount);
    }
}

// 简单工厂
public class PaymentFactory {
    public static Payment createPayment(String type) {
        switch (type) {
            case "alipay":   return new Alipay();
            case "wechat":   return new WechatPay();
            case "bank":     return new BankPay();
            default:
                throw new IllegalArgumentException("不支持的支付方式: " + type);
        }
    }
}

// 客户端调用
public class Client {
    public static void main(String[] args) {
        Payment payment = PaymentFactory.createPayment("alipay");
        payment.pay(199.99);
    }
}

优点 :将创建与使用分离,调用方只关心接口。

缺点 :新增支付方式需要修改工厂类的switch,违反开闭原则。适用于产品类型比较少且变化不频繁的场景。

简单工厂真的"简单"吗? 虽然叫"简单工厂",但在实际项目中很容易变复杂。比如你的switch分支从3个增长到20个,每个分支的创建逻辑从new Xxx()变成了一堆初始化代码------这时候简单工厂就变成了一个"上帝类",违背单一职责原则。另外,简单工厂通常用static方法,无法被子类重写,缺乏扩展点。如果预见到产品种类会频繁增加,应该从一开始就使用工厂方法模式。

二、工厂方法模式

定义一个创建对象的接口,让子类决定实例化哪个类:

java 复制代码
// 抽象工厂
public abstract class PaymentFactory {
    public abstract Payment createPayment();

    // 通用业务逻辑可以放在工厂里
    public void processPayment(double amount) {
        Payment payment = createPayment();
        payment.pay(amount);
        System.out.println("支付记录已保存");
    }
}

// 具体工厂
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 Client {
    public static void main(String[] args) {
        // 根据配置选择工厂
        PaymentFactory factory = new AlipayFactory();
        factory.processPayment(299.50);
    }
}

优势:新增支付方式只需添加新的产品和工厂子类,无需修改已有代码,符合开闭原则。每个工厂只负责创建一种产品,职责单一。

不足:类的数量成倍增长。如果产品类型非常多,会出现大量的工厂子类。

如何取舍? 当产品只有3-5种时,简单工厂的switch足够清晰;当产品达到10+种且仍在增长时,工厂方法的优势开始显现。但有一个折中方案------结合反射或SPI机制:将产品类名配置化,在工厂中用Class.forName(className).newInstance()动态创建,这样既保持了单一工厂的简洁,又不需要改代码。Spring的BeanFactory本质上就是这样做的:通过配置(XML或注解)注册Bean定义,工厂按需创建实例。

三、抽象工厂模式

抽象工厂创建一组相关相互依赖的产品族:

java 复制代码
// 产品族A:PC端UI组件
public interface Button {
    void render();
}
public interface TextField {
    void render();
}

// Windows实现
public class WinButton implements Button {
    @Override
    public void render() {
        System.out.println("渲染Windows风格按钮");
    }
}
public class WinTextField implements TextField {
    @Override
    public void render() {
        System.out.println("渲染Windows风格文本框");
    }
}

// Mac实现
public class MacButton implements Button {
    @Override
    public void render() {
        System.out.println("渲染Mac风格按钮");
    }
}
public class MacTextField implements TextField {
    @Override
    public void render() {
        System.out.println("渲染Mac风格文本框");
    }
}

// 抽象工厂:定义产品族接口
public interface UIFactory {
    Button createButton();
    TextField createTextField();
}

// 具体工厂:Windows产品族
public class WinUIFactory implements UIFactory {
    @Override
    public Button createButton() { return new WinButton(); }
    @Override
    public TextField createTextField() { return new WinTextField(); }
}

// 具体工厂:Mac产品族
public class MacUIFactory implements UIFactory {
    @Override
    public Button createButton() { return new MacButton(); }
    @Override
    public TextField createTextField() { return new MacTextField(); }
}

// 客户端
public class Application {
    private Button button;
    private TextField textField;

    public Application(UIFactory factory) {
        button = factory.createButton();
        textField = factory.createTextField();
    }

    public void renderUI() {
        button.render();
        textField.render();
    }

    public static void main(String[] args) {
        String os = System.getProperty("os.name");
        UIFactory factory;
        if (os.contains("Windows")) {
            factory = new WinUIFactory();
        } else {
            factory = new MacUIFactory();
        }
        Application app = new Application(factory);
        app.renderUI();
    }
}

抽象工厂的核心在于产品族的概念。当系统中有一组产品需要一起使用时(如Windows UI套件、Mac UI套件),用抽象工厂确保使用同一产品族内的组件,避免混搭。

抽象工厂 vs 工厂方法的决策:最简单的判断标准是看"产品是否成组出现"。如果支付和退款总是成对使用(支付宝支付+支付宝退款、微信支付+微信退款),就可以用抽象工厂;如果只有一种产品类型,工厂方法就够用。强行在不合适的场景使用抽象工厂,会导致过度设计------代码量翻倍却没有带来实际价值。

四、三种工厂模式对比

维度 简单工厂 工厂方法 抽象工厂
产品种类 1种(多个变体) 1种(多个变体) 多种(产品族)
新增产品 修改工厂类 添加子类 添加新工厂
符合开闭原则
复杂度
典型场景 JDBC DriverManager Spring BeanFactory MyBatis SqlSessionFactory

五、Spring中的工厂模式应用

Spring的IoC容器本质上是一个巨大的工厂:

java 复制代码
// 面试常问:BeanFactory和FactoryBean的区别
// BeanFactory是IoC容器的顶层接口,是工厂方法模式的体现
// FactoryBean是Spring提供的一个特殊Bean

// FactoryBean示例
@Component
public class HttpProxyFactoryBean implements FactoryBean<HttpProxy> {
    @Override
    public HttpProxy getObject() { return new HttpProxy("http://api.example.com"); }
    @Override
    public Class<?> getObjectType() { return HttpProxy.class; }
    @Override
    public boolean isSingleton() { return true; }
}

总结

工厂模式的核心思想是解耦对象的创建和使用。简单工厂是一个集中式工厂,适合产品类型固定的小项目;工厂方法将创建职责下放到子类,遵循开闭原则,适合需要灵活扩展的场景;抽象工厂处理产品族,适用于多套相关产品共存的系统。理解这三种模式的演进关系和应用场景,是设计好可扩展Java系统的关键。

终极理解:工厂模式解决的并不只是"怎么new对象"的问题------它解决的是"谁来负责创建对象"的职责分配问题。在面向对象设计中,"创建"本身就是一个值得封装的职责。把这层理解透了,你就能看懂Spring IoC为什么是工厂模式的集大成者:它不仅封装了创建,还管理了对象的整个生命周期(依赖注入、作用域、后置处理等)。

✅ 亮点总结

  • 简单工厂 → 工厂方法 → 抽象工厂的渐进式演进,展示了产品种类增加时设计的自然变化
  • 工厂方法遵循开闭原则------新增产品只需添加子工厂,无需修改已有代码
  • 抽象工厂确保产品族的一致性------Windows/Mac UI套件不能被混搭使用
  • BeanFactory vs FactoryBean 的辨析------面试高频题,前者是IoC容器,后者是特殊工厂Bean
  • 三种模式的维度对比表(产品种类、扩展方式、开闭原则、复杂度、典型场景)一目了然

适用场景

  • 多支付渠道接入------工厂方法为微信、支付宝、银联各自创建支付通道
  • 多数据库支持------抽象工厂创建MySQL/Oracle/PostgreSQL各自的Connection和Command对象
  • Spring Boot自动配置------@Configuration + @Bean 注解本质就是工厂方法的声明式写法

扩展方向

  • Spring IoC容器源码深入 :从 BeanFactoryApplicationContext 的层次结构理解工厂模式的框架级应用
  • 策略模式与工厂模式结合:工厂创建具体策略对象,策略执行不同行为,强强联合(推荐阅读下一篇:Java设计模式之代理模式)
  • SPI(Service Provider Interface)机制 :Java SPI与Spring SPI spring.factories 的工厂扩展点
相关推荐
uoKent1 小时前
项目整理——设计模式
设计模式·软件需求
凡人叶枫1 小时前
Effective C++ 条款32:确定你的 public 继承塑模出 is-a(是一种)关系
java·linux·开发语言·c++·嵌入式开发
小杨互联网1 小时前
Jar反编译逆向2.0教程实战
java·jar·java反编译·jar反编译·java逆向·源码还原
爱码少年1 小时前
Spring Boot 文件上传下载完整指南:从基础到高级实践
java·spring boot
码云骑士1 小时前
18-生成器不只是省内存(上)-yield的状态机模型与帧暂停
c语言·开发语言·python
我喜欢就喜欢1 小时前
C++ 连接 Ollama 本地大模型:从原生 HTTP 调用到高性能封装实践
开发语言·c++·http
Flittly1 小时前
【AgentScope Java新手村系列】(7)子Agent编排
java·spring boot·笔记·spring·ai
一个做软件开发的牛马2 小时前
Spring Boot Web 开发实战:RESTful API 设计、统一异常处理、参数校验与拦截器
java·后端
yurenpai(27届找实习中)2 小时前
Feed 流推送与附近商户:从推模式到 GeoHash,一条 Timeline 的完整旅程
java·数据库·oracle·feed