简单工厂模式:静态工厂方法、产品接口、具体产品,以及日志记录器创建案例
简单工厂模式,说白了就是用一个「工厂」帮你把对象创建的事全包了。你只需要告诉工厂你要什么,它给你造出来就行。我刚开始接触设计模式时,觉得这玩意儿太简单了,不就是把new关键字挪了个地方吗?后来在项目中踩过坑才明白——嗯,这背后其实藏着不少门道。
什么是简单工厂模式
简单工厂模式属于创建型模式,但它并不是GoF 23种设计模式中的一员。我个人习惯把它看作「编程入门级」的创建模式。它的核心思想是:用一个单独的工厂类来封装对象的创建逻辑。
你想想看,如果代码里到处是new XXX(),哪天产品说「我要换一个实现」,你得满世界找这些new语句。简单工厂就是把这些new集中到一处管理。
核心三要素:
- 产品接口(Product):定义产品的公共行为
- 具体产品(Concrete Product):实现产品接口的多个类
- 工厂类(Factory):包含静态方法,根据参数返回不同的产品实例
为什么叫「静态工厂方法」
因为工厂类里的创建方法通常是static的。你不需要new工厂对象,直接Factory.createProduct(type)就能拿到产品。我见过不少团队把这种模式直接叫做「静态工厂」,其实也没毛病。
这里有个小坑——静态方法不能被继承,也不能被重写。所以如果你未来需要扩展工厂行为,简单工厂就不太够用了。我曾经在一个项目中用简单工厂管理数据库连接,后来要支持多种数据库动态切换,不得不重构成了工厂方法模式。这个教训让我记住了:简单工厂适合产品种类固定、变化不频繁的场景。
日志记录器案例:从需求到实现
咱们用一个日志记录器的例子来走一遍。假设你的系统需要支持三种日志输出:控制台、文件、数据库。用户通过配置文件决定用哪一种。
先定义产品接口:
// 产品接口
public interface Logger {
void log(String message);
}
然后写三个具体产品:
// 具体产品:控制台日志
public class ConsoleLogger implements Logger {
@Override
public void log(String message) {
System.out.println("[Console] " + message);
}
}
// 具体产品:文件日志
public class FileLogger implements Logger {
@Override
public void log(String message) {
// 实际项目中这里会写文件
System.out.println("[File] " + message);
}
}
// 具体产品:数据库日志
public class DatabaseLogger implements Logger {
@Override
public void log(String message) {
// 实际项目中这里会写数据库
System.out.println("[Database] " + message);
}
}
接下来是核心——工厂类:
// 简单工厂
public class LoggerFactory {
// 静态工厂方法
public static Logger createLogger(String type) {
if (type == null || type.isEmpty()) {
return null;
}
switch (type.toLowerCase()) {
case "console":
return new ConsoleLogger();
case "file":
return new FileLogger();
case "database":
return new DatabaseLogger();
default:
throw new IllegalArgumentException("不支持的日志类型: " + type);
}
}
}
客户端使用起来就很简单了:
public class Client {
public static void main(String[] args) {
// 从配置文件读取类型
String logType = "file";
Logger logger = LoggerFactory.createLogger(logType);
logger.log("系统启动成功");
}
}
我的习惯:在实际项目中,我不会把类型字符串硬编码在代码里。通常我会从配置文件(properties、yaml或数据库)中读取,这样切换日志方式只需要改配置,不用改代码。
简单工厂的优缺点
| 优点 | 缺点 |
|---|---|
| 将对象的创建和使用分离,客户端不需要知道具体类名 | 工厂类职责过重,新增产品需要修改工厂类 |
| 代码集中管理,便于统一维护 | 违反了开闭原则(对扩展开放,对修改关闭) |
| 适合产品种类较少的场景 | 产品过多时,工厂方法会变得臃肿 |
避坑指南
我曾经接手过一个老项目,里面有一个工厂类,switch分支写了二十多个case。每次新增产品都要改这个类,而且经常有人忘记加break导致bug。后来我实在受不了,用反射+配置文件重构了它。
所以我的建议是:
- 产品种类少于5个时,用简单工厂很舒服
- 超过5个,考虑用工厂方法模式或反射机制
- 如果产品经常变化,别用简单工厂,否则你会被频繁修改工厂类逼疯
知识体系图
下面这张图帮你理清简单工厂模式的核心结构:
C++版本实现
如果你用C++,思路完全一样,只是语法不同。这里给个简版:
// 产品接口
class Logger {
public:
virtual ~Logger() {}
virtual void log(const std::string& message) = 0;
};
// 具体产品
class ConsoleLogger : public Logger {
public:
void log(const std::string& message) override {
std::cout << "[Console] " << message << std::endl;
}
};
// 工厂类
class LoggerFactory {
public:
static Logger* createLogger(const std::string& type) {
if (type == "console") return new ConsoleLogger();
if (type == "file") return new FileLogger();
if (type == "database") return new DatabaseLogger();
return nullptr;
}
};
注意:C++中工厂返回的是裸指针,记得在客户端用智能指针管理生命周期,否则容易内存泄漏。我见过太多C++新手在简单工厂上栽跟头——new出来的对象忘了delete。
什么时候用简单工厂
我个人总结了三用三不用:
- 用:产品种类少且稳定
- 用:客户端不需要知道具体产品类名
- 用:创建逻辑简单,不需要复杂初始化
- 不用:产品经常新增或删除
- 不用:产品创建过程复杂,需要依赖其他对象
- 不用:希望遵循开闭原则
说白了,简单工厂就是设计模式里的「入门款」。它简单、直观、好用,但别指望它能解决所有创建问题。我建议你在小项目或模块内部使用它,大型框架层面还是考虑更灵活的模式。
公众号:蓝海资料掘金营,微信deep3321