工厂模式:从简单到抽象,再到JDBC实战
工厂模式,说白了就是帮你「生产对象」的套路。
我刚开始写代码那会儿,经常直接 new 一堆对象。后来项目大了,发现到处 new 简直就是灾难——改一个类名,全工程都得跟着改。嗯,工厂模式就是来解决这个问题的。
简单工厂模式:最朴素的工厂
先看一个最基础的场景。假设你要创建不同类型的数据库连接:
public class ConnectionFactory {
public static Connection createConnection(String type) {
if ("mysql".equals(type)) {
return new MySQLConnection();
} else if ("oracle".equals(type)) {
return new OracleConnection();
} else if ("postgresql".equals(type)) {
return new PostgreSQLConnection();
}
throw new IllegalArgumentException("不支持的数据库类型");
}
}
客户端调用就清爽多了:
Connection conn = ConnectionFactory.createConnection("mysql");
简单工厂的核心就一句话:用一个静态方法封装对象创建逻辑。
优点:把创建逻辑集中管理,客户端不用关心具体实现类。
缺点:每次新增类型都要改工厂类,违反了「开闭原则」。
我在项目中遇到过这种情况:一开始只有MySQL和Oracle,后来加了PostgreSQL、SQLite、H2……工厂类越改越臃肿。这时候就该考虑升级了。
工厂方法模式:把创建权交给子类
工厂方法模式的核心思想是:定义一个创建对象的接口,让子类决定实例化哪个类。
说白了,就是把简单工厂里的那个大 if-else 拆成多个子工厂:
// 抽象工厂
public interface DatabaseFactory {
Connection createConnection();
}
// 具体工厂
public class MySQLFactory implements DatabaseFactory {
@Override
public Connection createConnection() {
return new MySQLConnection();
}
}
public class OracleFactory implements DatabaseFactory {
@Override
public Connection createConnection() {
return new OracleConnection();
}
}
// 客户端
DatabaseFactory factory = new MySQLFactory();
Connection conn = factory.createConnection();
你看,新增一种数据库,只需要新增一个工厂类和一个产品类,完全不用改已有代码。这就是开闭原则的体现。
我的习惯:当产品类型可能频繁扩展时,我优先考虑工厂方法模式。它虽然多了一些类,但维护成本低很多。
抽象工厂模式:生产产品族
如果工厂方法模式是生产「单一产品」,那抽象工厂模式就是生产「一系列相关产品」。
举个例子。一个UI组件库,需要同时生产按钮、输入框、弹窗。不同操作系统(Windows、macOS、Linux)的样式完全不同。这时候抽象工厂就派上用场了:
// 抽象产品
public interface Button { void render(); }
public interface Input { void render(); }
public interface Dialog { void render(); }
// 抽象工厂
public interface UIFactory {
Button createButton();
Input createInput();
Dialog createDialog();
}
// 具体工厂 - Windows风格
public class WindowsUIFactory implements UIFactory {
@Override
public Button createButton() { return new WindowsButton(); }
@Override
public Input createInput() { return new WindowsInput(); }
@Override
public Dialog createDialog() { return new WindowsDialog(); }
}
// 具体工厂 - macOS风格
public class MacUIFactory implements UIFactory {
@Override
public Button createButton() { return new MacButton(); }
@Override
public Input createInput() { return new MacInput(); }
@Override
public Dialog createDialog() { return new MacDialog(); }
}
抽象工厂的关键在于:保证同一工厂生产的产品之间是兼容的。你不会把Windows的按钮和macOS的弹窗混在一起用。
我曾经踩过的坑:抽象工厂模式容易过度设计。如果你的产品族不会变化,或者只有一两个产品,用简单工厂就够了。别为了模式而模式。
三种工厂模式对比
| 模式 | 核心思想 | 适用场景 | 扩展方式 |
|---|---|---|---|
| 简单工厂 | 一个工厂类,根据参数创建不同产品 | 产品种类少,且不常变化 | 修改工厂类 |
| 工厂方法 | 一个抽象工厂,多个具体工厂 | 产品种类多,且经常扩展 | 新增工厂类 |
| 抽象工厂 | 一个抽象工厂,生产多个产品族 | 需要创建一系列相关产品 | 新增工厂类 |
工厂模式在JDBC中的应用
说到工厂模式的实际应用,JDBC绝对是个经典案例。你想想看,Java程序要连接各种数据库——MySQL、Oracle、PostgreSQL……但JDBC API却是一套统一的接口。这是怎么做到的?
答案就在 DriverManager 和 Driver 接口里。
JDBC使用了简单工厂模式:
// 客户端代码
Connection conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/mydb",
"user",
"password"
);
DriverManager.getConnection() 内部会遍历所有注册的 Driver 实现类,找到能处理该URL的驱动,然后创建连接。这个过程对开发者完全透明。
同时,JDBC也体现了工厂方法模式的思想:
// 每个数据库驱动都有自己的工厂
public class MySQLDriver implements Driver {
@Override
public Connection connect(String url, Properties info) {
// 返回 MySQLConnection 实例
return new MySQLConnection(url, info);
}
}
public class OracleDriver implements Driver {
@Override
public Connection connect(String url, Properties info) {
// 返回 OracleConnection 实例
return new OracleConnection(url, info);
}
}
你看,Driver 接口就是抽象工厂,每个数据库厂商实现自己的 Driver,生产自己的 Connection。这就是工厂方法模式。
JDBC的设计精髓:
- 客户端只依赖
java.sql包下的接口 - 具体实现由各数据库厂商提供
- 通过
DriverManager统一管理 - 新增数据库只需添加驱动jar包,无需改代码
我个人觉得,JDBC是理解工厂模式最好的实战案例。它把简单工厂和工厂方法结合得恰到好处,既保证了扩展性,又保持了API的简洁。
知识体系总览
下面这张图帮你理清三种工厂模式的关系和演进路径:
从简单工厂到工厂方法,再到抽象工厂,其实是一个「从简单到灵活」的演进过程。JDBC则展示了如何在实际框架中灵活运用这些模式。
我的建议:刚开始学设计模式时,别想着一步到位用抽象工厂。从简单工厂开始,等发现它不够用了,再重构到工厂方法。代码是慢慢长出来的,不是设计出来的。
公众号:蓝海资料掘金营,微信deep3321