工厂方法模式(Factory Method)------ 你说要啥,我给你造
大白话解释
你去奶茶店点单,说"要一杯珍珠奶茶",店员就给你做一杯。你不需要知道怎么煮珍珠、怎么调茶、怎么打奶盖------你只管说"要什么",工厂负责"怎么造"。
工厂方法模式:定义一个创建对象的接口,但让子类决定实例化哪个类。创建的细节交给子类,调用者只关心拿到的结果。
常见场景:
- 日志系统(文件日志、数据库日志、控制台日志)
- UI 组件(Windows 风格按钮、Mac 风格按钮)
- 数据库驱动(MySQL、PostgreSQL、SQLite 连接器)
核心思路
- 定义产品接口(所有产品长啥样)
- 定义工厂接口(工厂该干什么)
- 具体产品类实现产品接口
- 具体工厂类实现工厂接口,负责创建对应产品
C++ 代码示例
cpp
#include <iostream>
#include <memory>
#include <string>
// =====================
// 第一步:定义产品接口
// =====================
class Logger {
public:
virtual ~Logger() = default;
virtual void log(const std::string& message) = 0;
};
// =====================
// 第二步:具体产品
// =====================
// 控制台日志
class ConsoleLogger : public Logger {
public:
void log(const std::string& message) override {
std::cout << "[控制台] " << message << "\n";
}
};
// 文件日志
class FileLogger : public Logger {
public:
void log(const std::string& message) override {
std::cout << "[文件] 写入 app.log: " << message << "\n";
// 实际项目中这里会真正写文件
}
};
// 数据库日志
class DatabaseLogger : public Logger {
public:
void log(const std::string& message) override {
std::cout << "[数据库] INSERT INTO logs: " << message << "\n";
}
};
// =====================
// 第三步:定义工厂接口
// =====================
class LoggerFactory {
public:
virtual ~LoggerFactory() = default;
// 工厂方法:子类来实现具体创建逻辑
virtual std::unique_ptr<Logger> createLogger() = 0;
// 工厂可以包含通用业务逻辑
void logWithTimestamp(const std::string& message) {
auto logger = createLogger();
logger->log("[2026-01-01 12:00:00] " + message);
}
};
// =====================
// 第四步:具体工厂
// =====================
class ConsoleLoggerFactory : public LoggerFactory {
public:
std::unique_ptr<Logger> createLogger() override {
return std::make_unique<ConsoleLogger>();
}
};
class FileLoggerFactory : public LoggerFactory {
public:
std::unique_ptr<Logger> createLogger() override {
return std::make_unique<FileLogger>();
}
};
class DatabaseLoggerFactory : public LoggerFactory {
public:
std::unique_ptr<Logger> createLogger() override {
return std::make_unique<DatabaseLogger>();
}
};
// =====================
// 使用示例
// =====================
void doBusinessLogic(LoggerFactory& factory) {
auto logger = factory.createLogger();
logger->log("用户登录成功");
logger->log("订单创建完毕");
}
int main() {
std::cout << "=== 开发环境(控制台日志)===\n";
ConsoleLoggerFactory consoleFactory;
doBusinessLogic(consoleFactory);
std::cout << "\n=== 生产环境(文件日志)===\n";
FileLoggerFactory fileFactory;
doBusinessLogic(fileFactory);
std::cout << "\n=== 审计环境(数据库日志)===\n";
DatabaseLoggerFactory dbFactory;
doBusinessLogic(dbFactory);
std::cout << "\n=== 带时间戳的日志 ===\n";
consoleFactory.logWithTimestamp("系统启动完成");
return 0;
}
输出:
plaintext
=== 开发环境(控制台日志)===
[控制台] 用户登录成功
[控制台] 订单创建完毕
=== 生产环境(文件日志)===
[文件] 写入 app.log: 用户登录成功
[文件] 写入 app.log: 订单创建完毕
=== 审计环境(数据库日志)===
[数据库] INSERT INTO logs: 用户登录成功
[数据库] INSERT INTO logs: 订单创建完毕
=== 带时间戳的日志 ===
[控制台] [2026-01-01 12:00:00] 系统启动完成
和简单工厂的区别
| 简单工厂 | 工厂方法 | |
|---|---|---|
| 结构 | 一个工厂 if-else 搞定一切 | 每种产品对应一个工厂子类 |
| 扩展 | 加新产品要改工厂代码 | 加新产品只需新增工厂子类 |
| 符合 | ❌ 违反开闭原则 | ✅ 符合开闭原则 |
优缺点
| 说明 | |
|---|---|
| ✅ 优点 | 创建和使用分离,调用方不依赖具体类 |
| ✅ 优点 | 扩展新产品无需修改已有代码(开闭原则) |
| ❌ 缺点 | 每增加一个产品就要增加一对产品类+工厂类 |
| ❌ 缺点 | 类的数量会膨胀 |