对象工厂模式:从基础到Spring FactoryBean
对象工厂模式,说白了就是帮你创建对象的。你可能会想:「这不就是new一下吗?」嗯,在简单场景下确实如此。但当你面对复杂的对象创建逻辑、需要解耦调用方和实现类时,工厂模式就成了你的好帮手。
我个人习惯把工厂模式看作「对象的代工厂」。你告诉工厂你要什么,它给你造出来,你不用关心造的过程有多复杂。今天我们就从最基础的工厂接口讲起,一直聊到Spring的FactoryBean。
工厂接口:定义契约
工厂接口是整个模式的基石。它定义了一个统一的创建方法,所有具体工厂都必须实现它。
// Java版本
public interface ObjectFactory<T> {
T create();
}
// C++版本
template<typename T>
class ObjectFactory {
public:
virtual ~ObjectFactory() = default;
virtual T* create() = 0;
};
为什么要有接口?我在项目中遇到过这样的场景:一开始直接new对象,后来需求变了,需要从配置文件读取参数来创建。如果当初用了工厂接口,只需要新增一个实现类,调用方代码一行都不用改。这就是面向接口编程的魅力。
具体工厂:实现创建逻辑
具体工厂负责实际的创建过程。这里可以包含复杂的初始化逻辑、参数校验、缓存策略等。
// Java版本
public class DatabaseConnectionFactory implements ObjectFactory<Connection> {
private final String url;
private final String username;
private final String password;
public DatabaseConnectionFactory(String url, String username, String password) {
this.url = url;
this.username = username;
this.password = password;
}
@Override
public Connection create() {
// 这里可以加入连接池、重试机制等复杂逻辑
return DriverManager.getConnection(url, username, password);
}
}
// C++版本
class MySqlConnectionFactory : public ObjectFactory<DatabaseConnection> {
private:
std::string host_;
int port_;
std::string user_;
std::string password_;
public:
MySqlConnectionFactory(const std::string& host, int port,
const std::string& user, const std::string& password)
: host_(host), port_(port), user_(user), password_(password) {}
DatabaseConnection* create() override {
// 实际项目中这里会调用数据库驱动库
return new DatabaseConnection(host_, port_, user_, password_);
}
};
个人经验:具体工厂里不要只做简单的new。我习惯把对象生命周期管理、依赖注入、配置加载都放在工厂里。这样工厂就成了对象的「全权代理人」。
产品创建:从简单到复杂
产品创建可以很简单,也可以很复杂。关键在于:调用方不需要知道这些复杂性。
// 使用示例
public class Application {
private final ObjectFactory<ReportGenerator> factory;
public Application(ObjectFactory<ReportGenerator> factory) {
this.factory = factory;
}
public void generateReport() {
ReportGenerator generator = factory.create();
generator.generate();
}
}
// 调用方只需要知道工厂接口,不需要知道具体实现
public static void main(String[] args) {
ObjectFactory<ReportGenerator> factory = new PdfReportFactory();
Application app = new Application(factory);
app.generateReport();
}
你想想看,如果哪天要把PDF换成Excel,只需要换一个工厂实现。调用方Application完全不用改。这就是工厂模式的核心价值——解耦。
Spring FactoryBean:工厂模式的进阶应用
说到工厂模式,就不得不提Spring的FactoryBean。它本质上就是工厂模式在Spring IoC容器中的具体实现。
// Spring FactoryBean示例
@Component
public class MyServiceFactoryBean implements FactoryBean<MyService> {
@Override
public MyService getObject() throws Exception {
// 这里可以执行复杂的初始化逻辑
MyService service = new MyService();
service.setConfig(loadConfig());
service.init();
return service;
}
@Override
public Class<?> getObjectType() {
return MyService.class;
}
@Override
public boolean isSingleton() {
return true; // 默认返回单例
}
private Config loadConfig() {
// 从配置文件或数据库加载配置
return new Config();
}
}
关键点:FactoryBean和普通Bean的区别在于:普通Bean直接由Spring容器管理生命周期,而FactoryBean允许你在创建过程中插入自定义逻辑。Spring容器管理的是FactoryBean本身,而getObject()返回的是你真正需要的对象。
我曾经踩过一个坑:在FactoryBean的getObject()方法里调用了其他Bean的方法,结果造成了循环依赖。嗯,这里要注意——FactoryBean的创建时机比普通Bean早,如果依赖了其他需要延迟初始化的Bean,很容易出问题。
工厂模式的核心结构
下面这张图展示了对象工厂模式的整体结构,以及Spring FactoryBean在其中的位置:
工厂模式的应用场景
| 场景 | 说明 | 推荐使用 |
|---|---|---|
| 对象创建逻辑复杂 | 需要多步初始化、参数校验、资源加载 | 工厂模式 |
| 需要解耦调用方和实现类 | 调用方不关心具体实现,只依赖接口 | 工厂模式 + 依赖注入 |
| 需要集中管理对象生命周期 | 单例、池化、延迟加载等策略 | FactoryBean 或自定义工厂 |
| 需要根据配置动态创建对象 | 从配置文件、数据库读取创建参数 | 工厂模式 + 反射/模板 |
避坑指南:我曾经在一个项目里过度使用工厂模式,每个类都配一个工厂,结果代码量翻了三倍。记住:工厂模式是为了解决复杂创建问题,如果只是简单的new,直接用就好了。不要为了模式而模式。
工厂模式与Spring FactoryBean的对比
| 特性 | 传统工厂模式 | Spring FactoryBean |
|---|---|---|
| 管理方式 | 手动管理工厂实例 | 由Spring IoC容器管理 |
| 生命周期 | 需要手动控制 | 自动管理,支持单例/原型 |
| 依赖注入 | 需要手动注入依赖 | 自动注入,支持AOP |
| 灵活性 | 完全自定义 | 受限于Spring容器 |
| 适用场景 | 非Spring项目或需要完全控制 | Spring项目中的复杂Bean创建 |
我个人更倾向于在Spring项目中使用FactoryBean来处理那些「创建过程比较复杂」的Bean。比如需要从数据库加载配置、需要执行远程调用初始化、或者需要根据环境动态选择实现类。这些场景用FactoryBean再合适不过了。
小技巧:如果你在Spring中只是需要根据配置创建不同的实现,可以考虑用@Conditional注解,比FactoryBean更轻量。FactoryBean更适合那些创建逻辑本身就很复杂的场景。
好了,关于对象工厂模式就聊到这里。从基础的工厂接口到Spring的FactoryBean,核心思想始终没变:把对象的创建过程封装起来,让调用方专注于业务逻辑。记住这个原则,你就能用好工厂模式。