工厂方法模式:从简单工厂到真正的工厂

说起工厂方法模式,我得先坦白一件事。我刚开始写代码那会儿,觉得工厂模式就是个花架子——明明 new 一下就能搞定的事,非要绕个弯子。直到有一次,我在一个电商项目里被坑惨了……嗯,从那以后,我再也不敢小看它了。

今天咱们就好好聊聊工厂方法模式。我会从简单工厂讲起,一步步带你看到工厂方法是怎么诞生的,以及它在 JDBC 里是怎么用的。

1. 简单工厂:最朴素的“工厂”

先看一个场景。假设你要写一个日志系统,支持文件日志和数据库日志。最直接的写法是这样的:

// 日志接口
public interface Logger {
    void log(String message);
}

// 文件日志
public class FileLogger implements Logger {
    public void log(String message) {
        System.out.println("写入文件: " + message);
    }
}

// 数据库日志
public class DatabaseLogger implements Logger {
    public void log(String message) {
        System.out.println("写入数据库: " + message);
    }
}

那客户端怎么用呢?最笨的办法就是直接 new:

Logger logger = new FileLogger();
logger.log("用户登录了");

问题来了。如果哪天你想把文件日志换成数据库日志,你得改所有 new 的地方。项目小还好说,项目大了,这简直就是噩梦。

于是简单工厂模式登场了:

public class LoggerFactory {
    public static Logger createLogger(String type) {
        if ("file".equals(type)) {
            return new FileLogger();
        } else if ("database".equals(type)) {
            return new DatabaseLogger();
        }
        throw new IllegalArgumentException("未知的日志类型");
    }
}

// 客户端调用
Logger logger = LoggerFactory.createLogger("file");
logger.log("用户登录了");

这样,客户端只需要知道一个工厂类,不用关心具体实现。想换日志类型?改一下参数就行。

简单工厂的核心:把对象的创建逻辑集中到一个地方。客户端不直接 new,而是通过工厂来获取对象。

2. 简单工厂的痛点

简单工厂好用吗?说实话,小项目里挺好用的。但我在实际项目中遇到过一个问题——开闭原则被破坏了

什么意思呢?假如现在要加一个“云日志”类型,你得去修改 LoggerFactory 的 createLogger 方法,加一个 else if。每次新增产品,都要改工厂类。这违反了“对扩展开放,对修改关闭”的原则。

而且,随着产品种类增多,这个工厂类会变得臃肿不堪。我曾经接手过一个项目,里面的简单工厂有上百个 if-else,看得我头皮发麻。

避坑指南:我曾经在一个支付系统中用简单工厂处理了十几种支付方式。结果每次新增支付渠道,都要改工厂类,改一次测试一次,烦不胜烦。后来我重构成了工厂方法模式,世界清净了。

3. 工厂方法模式:让子类决定创建什么

工厂方法模式是怎么解决这个问题的?说白了,就是把“创建对象”这个动作延迟到子类去实现。

看代码:

// 抽象工厂
public abstract class LoggerFactory {
    public abstract Logger createLogger();
    
    // 公共的业务方法
    public void logMessage(String message) {
        Logger logger = createLogger();
        logger.log(message);
    }
}

// 具体工厂 - 文件日志
public class FileLoggerFactory extends LoggerFactory {
    public Logger createLogger() {
        return new FileLogger();
    }
}

// 具体工厂 - 数据库日志
public class DatabaseLoggerFactory extends LoggerFactory {
    public Logger createLogger() {
        return new DatabaseLogger();
    }
}

// 客户端
LoggerFactory factory = new FileLoggerFactory();
factory.logMessage("用户登录了");

看到区别了吗?现在每个产品都有自己对应的工厂。想加“云日志”?直接加一个 CloudLogger 和 CloudLoggerFactory 就行,不用改任何现有代码。

我的习惯:我个人习惯在工厂方法模式中,把公共的业务逻辑放在抽象工厂里。比如上面的 logMessage 方法,所有日志记录前的格式化、后处理都可以放在这里,子类工厂只需要关心“创建什么对象”。

4. 适用场景:什么时候用工厂方法?

不是所有地方都需要工厂方法。我总结了几个典型的场景:

场景 说明 例子
产品种类频繁扩展 经常要加新的产品类型 支付渠道、消息推送
产品创建逻辑复杂 创建对象需要一堆初始化步骤 数据库连接、网络客户端
客户端不需要知道具体类 只关心接口,不关心实现 日志系统、缓存系统
框架设计 让框架使用者扩展功能 Spring BeanFactory

你想想看,如果你的项目里只有两三种产品,而且基本不会变,那简单工厂完全够用。别为了用模式而用模式,这是很多新手容易犯的毛病。

5. JDBC 中的工厂模式

说到 JDBC,这可是工厂方法模式的经典案例。咱们来看看 JDBC 是怎么用工厂模式的。

JDBC 的核心接口是 java.sql.Connection,但不同的数据库(MySQL、Oracle、PostgreSQL)都有自己的实现。那问题来了:客户端怎么获取到正确的 Connection 实现?

答案就是 DriverManager——它其实就是一个简单工厂:

// 客户端代码
Connection conn = DriverManager.getConnection(
    "jdbc:mysql://localhost:3306/mydb", 
    "root", 
    "password"
);

但这里有个更深的层次。每个数据库驱动(比如 MySQL 的 com.mysql.cj.jdbc.Driver)都实现了 java.sql.Driver 接口。当驱动被加载时,它会把自己注册到 DriverManager 里。

Driver 接口的 connect 方法,本质上就是一个工厂方法:

// 这是 Driver 接口的核心方法
public interface Driver {
    Connection connect(String url, Properties info) throws SQLException;
}

// MySQL 的实现
public class Driver implements java.sql.Driver {
    public Connection connect(String url, Properties info) {
        // 解析 URL,创建 MySQL 的 Connection 实现
        return new ConnectionImpl(url, info);
    }
}

// Oracle 的实现
public class OracleDriver implements java.sql.Driver {
    public Connection connect(String url, Properties info) {
        // 解析 URL,创建 Oracle 的 Connection 实现
        return new OracleConnection(url, info);
    }
}

看到了吗?每个数据库驱动都是一个具体的工厂,负责创建自己的 Connection 对象。这就是工厂方法模式在 JDBC 中的体现。

JDBC 的工厂模式层次

  • 抽象产品Connection 接口
  • 具体产品ConnectionImplOracleConnection
  • 抽象工厂Driver 接口
  • 具体工厂com.mysql.cj.jdbc.Driveroracle.jdbc.OracleDriver
  • 客户端:通过 DriverManager 获取连接

6. 知识体系总览

下面这张图帮你理清工厂方法模式的核心脉络:

工厂方法模式知识体系 简单工厂 • 一个工厂类创建所有产品 • 违反开闭原则 • 适合产品种类少且稳定 • 扩展需修改工厂类 工厂方法 • 每个产品对应一个工厂 • 符合开闭原则 • 适合产品频繁扩展 • 扩展只需新增类 JDBC 案例 • Driver 接口 = 抽象工厂 • 各数据库驱动 = 具体工厂 • Connection = 抽象产品 • DriverManager = 客户端 核心要点总结 1. 简单工厂 vs 工厂方法:核心区别在于“谁来负责创建” 2. 工厂方法把创建逻辑延迟到子类,符合开闭原则 3. 适用场景:产品种类多、变化频繁、创建逻辑复杂 4. JDBC 是工厂方法模式的经典实践,值得反复品味 5. 不要滥用:产品少且稳定时,简单工厂更合适

7. 总结

工厂方法模式,说白了就是让“创建对象”这件事变得更有弹性。它把变化的点封装起来,让系统更容易扩展。

我个人觉得,理解工厂方法的关键在于想清楚一个问题:谁应该负责创建对象?简单工厂把责任集中在一个类上,工厂方法把责任分散到多个子类上。没有绝对的好坏,只有合不合适。

最后送你一句话:设计模式是工具,不是教条。用对了是神器,用错了是累赘。多在实践中体会,慢慢你就会有感觉了。


公众号:蓝海资料掘金营,微信deep3321