Java工厂方法案例如何编写:从基础到高级的完整指南
📑 目录导读
工厂方法模式是什么?
工厂方法(Factory Method)是Java中最常用的创建型设计模式之一,它定义一个创建对象的接口,但让子类决定实例化哪个类,简单说就是:把对象的创建延迟到子类中。

很多开发者会问:“直接new对象不就好了,为什么要这么复杂?”
核心原因:当创建对象的过程需要根据不同条件变化,或者对象创建逻辑复杂时,工厂方法能让你不修改现有代码就能扩展新的产品类型,符合开闭原则。
为什么要用工厂方法?
❌ 不用工厂的痛点:
// 客户端直接依赖具体类
if (type.equals("file")) {
Logger logger = new FileLogger();
} else if (type.equals("console")) {
Logger logger = new ConsoleLogger();
}
- 每新增一种日志类型,就要修改这段if-else代码
- 违反开闭原则,容易出错
✅ 使用工厂方法的好处:
- 解耦:客户端只依赖抽象接口,不依赖具体实现
- 扩展性:新增产品类无需修改已有代码
- 复用性:创建逻辑集中在工厂子类中
- 遵守单一职责:每个类只负责自己的创建逻辑
基础案例:日志记录器
1 场景描述
假设我们正在开发一个应用,需要支持多种日志记录方式:文件日志、控制台日志、数据库日志,未来可能还会增加网络日志。
2 实现步骤
第一步:定义产品接口
public interface Logger {
void log(String message);
}
第二步:实现具体产品
public class FileLogger implements Logger {
@Override
public void log(String message) {
System.out.println("写入文件: " + message);
// 实际的文件写入逻辑
}
}
public class ConsoleLogger implements Logger {
@Override
public void log(String message) {
System.out.println("控制台输出: " + message);
}
}
第三步:定义工厂接口(核心)
public abstract class LoggerFactory {
public abstract Logger createLogger();
// 可选:公共业务方法
public void logMessage(String message) {
Logger logger = createLogger();
logger.log(message);
}
}
第四步:实现具体工厂
public class FileLoggerFactory extends LoggerFactory {
@Override
public Logger createLogger() {
// 这里可以做复杂的初始化配置
return new FileLogger();
}
}
public class ConsoleLoggerFactory extends LoggerFactory {
@Override
public Logger createLogger() {
return new ConsoleLogger();
}
}
第五步:客户端使用
public class Client {
public static void main(String[] args) {
LoggerFactory factory = new FileLoggerFactory();
Logger logger = factory.createLogger();
logger.log("这是文件日志");
// 切换只需改一行
factory = new ConsoleLoggerFactory();
factory.logMessage("这是控制台日志");
}
}
3 扩展新类型
现在需要增加数据库日志,只需:
- 创建
DatabaseLogger implements Logger - 创建
DatabaseLoggerFactory extends LoggerFactory - 客户端直接使用新工厂即可,无需修改任何已有代码
进阶案例:数据库连接工厂
1 场景描述
开发一个支持多种数据库的中间件:MySQL、PostgreSQL、Oracle,每个数据库的连接参数和驱动加载都不同。
2 更复杂的实现
产品接口
public interface DatabaseConnection {
void connect();
void executeQuery(String sql);
void disconnect();
}
工厂接口(带参数)
public abstract class DatabaseFactory {
public abstract DatabaseConnection createConnection(String url, String username, String password);
// 模板方法模式结合工厂方法
public DatabaseConnection createAndConnect(String url, String username, String password) {
DatabaseConnection conn = createConnection(url, username, password);
conn.connect();
return conn;
}
}
具体工厂
public class MySQLFactory extends DatabaseFactory {
@Override
public DatabaseConnection createConnection(String url, String username, String password) {
// 验证参数
if (url == null || url.isEmpty()) {
throw new IllegalArgumentException("URL不能为空");
}
// 配置MySQL特有参数
Properties props = new Properties();
props.setProperty("useSSL", "false");
props.setProperty("characterEncoding", "UTF-8");
return new MySQLConnection(url, username, password, props);
}
}
客户端通过配置文件选择工厂
public class DatabaseManager {
private DatabaseFactory factory;
public DatabaseManager(String dbType) {
// 通过反射或配置动态加载工厂
if ("mysql".equalsIgnoreCase(dbType)) {
factory = new MySQLFactory();
} else if ("postgresql".equalsIgnoreCase(dbType)) {
factory = new PostgreSQLFactory();
} else {
throw new IllegalArgumentException("不支持的数据库类型");
}
}
public void execute(String url, String user, String pass, String sql) {
DatabaseConnection conn = factory.createAndConnect(url, user, pass);
conn.executeQuery(sql);
conn.disconnect();
}
}
工厂方法 VS 简单工厂
| 对比维度 | 简单工厂 | 工厂方法 |
|---|---|---|
| 创建方式 | 一个工厂类负责创建所有产品 | 每个产品对应一个工厂子类 |
| 扩展性 | 新增产品需修改工厂类 | 新增产品只需增加新子类 |
| 复杂度 | 简单,但违背开闭原则 | 稍复杂,完全符合开闭原则 |
| 适用场景 | 产品种类少且固定 | 产品种类多且经常扩展 |
一个真实的例子:
- 简单工厂:就像一家餐馆的“主厨”决定今天做什么菜,所有菜都他做
- 工厂方法:就像连锁店,每家分店有自己的主厨(子类),但都遵循总部的配方标准(抽象工厂)
常见误区与最佳实践
❌ 常见错误
- 滥用工厂方法:当产品类型固定不变时,简单工厂更合适
- 工厂类过于复杂:把业务逻辑也塞进工厂里
- 忘记使用抽象:客户端直接引用具体工厂类,没有面向接口编程
✅ 最佳实践
- 工厂命名规范:
XxxFactory,XxxBuilder(如果涉及构建过程) - 结合配置使用:从配置文件读取工厂类名,通过反射实例化
- 防止工厂泛滥:当一个工厂只创建一个产品时,考虑使用泛型
- 与DI容器结合:Spring的
FactoryBean接口就是工厂方法的优秀实现
⚡ 性能考虑
- 如果创建对象频繁,且对象轻量,考虑对象池+工厂方法
- 工厂方法会增加类数量,但JVM对少量类的加载开销可以忽略
问答环节
Q1: 工厂方法是不是每次都要创建一个新工厂?
不一定,工厂实例可以复用,特别是当工厂是无状态时,可以将工厂设为单例或使用池化技术。
Q2: 工厂方法和抽象工厂有什么区别?
关键区别:
- 工厂方法:生产单一产品,通过继承扩展
- 抽象工厂:生产产品族(多个相关产品),通过组合扩展
Q3: 可以用Java 8的Lambda简化工厂方法吗?
可以,对于简单的工厂逻辑:
Map<String, Supplier<Logger>> factoryMap = new HashMap<>();
factoryMap.put("file", FileLogger::new);
factoryMap.put("console", ConsoleLogger::new);
Logger logger = factoryMap.get(type).get();
但复杂初始化逻辑仍需要专门的工厂类。
Q4: 工厂方法和策略模式很像,怎么区分?
本质区别:
- 工厂方法:关注对象创建
- 策略模式:关注算法行为
Q5: 有没有开源框架使用了工厂方法?
非常多:
- Spring的
BeanFactory和FactoryBean - JDBC的
DriverManager.getConnection()(虽然更多是简单工厂风格) - Log4j2的
LoggerFactory
工厂方法模式是Java开发者必须掌握的设计模式之一,它通过将对象的创建过程封装到子类中,让代码更加灵活和可扩展,在实际项目中,我们通常不会只用纯工厂方法,而是会结合模板方法、策略模式、反射技术来构建更强大的创建机制。
设计模式是工具,不是教条,当你的代码中出现重复的“new”操作,或者需要频繁修改创建逻辑时,就是引入工厂方法的最佳时机。
建议你在自己的练习项目中有意识地使用工厂方法重构现有的“硬编码”创建逻辑,体会它带来的灵活性和可维护性的提升。