💻CS 设计模式
工厂模式
难度:⭐⭐ | 高频指数:🔥🔥
面试回答
常见问法
- 简单工厂、工厂方法、抽象工厂有什么区别?
- 什么时候用工厂模式?
- 工厂模式解决什么问题?
- C++ 中工厂模式怎么配合智能指针?
- 工厂模式和直接 new 有什么区别?
回答
工厂模式的核心思想是将对象的创建和使用分离,调用者不需要知道具体创建了哪个类的对象。
三种工厂模式:
| 模式 | 特点 | 适用场景 |
|---|---|---|
| 简单工厂 | 一个工厂类,根据参数创建不同产品 | 产品种类少、不常变化 |
| 工厂方法 | 每个产品对应一个工厂类 | 产品种类多、需要扩展 |
| 抽象工厂 | 创建一组相关产品(产品族) | 多个产品有关联关系 |
简单工厂示例:
class Shape {
public:
virtual void draw() = 0;
virtual ~Shape() = default;
};
class Circle : public Shape { /* ... */ };
class Rectangle : public Shape { /* ... */ };
// 简单工厂
class ShapeFactory {
public:
static std::unique_ptr<Shape> create(const std::string& type) {
if (type == "circle") return std::make_unique<Circle>();
if (type == "rect") return std::make_unique<Rectangle>();
return nullptr;
}
};
// 使用:调用者不需要知道具体类
auto shape = ShapeFactory::create("circle");
shape->draw();
工厂模式解决的问题:
- 解耦创建和使用——调用者不依赖具体类
- 集中管理创建逻辑——新增产品只改工厂
- 配合多态——返回基类指针,运行时决定具体类型
追问
1. 简单工厂的缺点?
违反开闭原则:每新增一种产品,都要修改工厂类的 if-else。产品种类多时代码臃肿。
2. 工厂方法怎么解决这个问题?
每个产品对应一个工厂类,新增产品只需新增工厂,不修改已有代码。
3. 什么时候用工厂,什么时候直接 new?
- 创建逻辑简单、类型固定 → 直接 new
- 需要根据配置/参数决定类型 → 工厂
- 创建过程复杂(需要初始化、注册等) → 工厂
- 需要解耦调用者和具体类 → 工厂
原理展开
1. 工厂方法模式
// 产品接口
class Logger {
public:
virtual void log(const std::string& msg) = 0;
virtual ~Logger() = default;
};
class FileLogger : public Logger {
public:
void log(const std::string& msg) override {
// 写入文件
}
};
class ConsoleLogger : public Logger {
public:
void log(const std::string& msg) override {
// 输出到控制台
}
};
// 工厂接口
class LoggerFactory {
public:
virtual std::unique_ptr<Logger> createLogger() = 0;
virtual ~LoggerFactory() = default;
};
// 具体工厂
class FileLoggerFactory : public LoggerFactory {
public:
std::unique_ptr<Logger> createLogger() override {
return std::make_unique<FileLogger>();
}
};
class ConsoleLoggerFactory : public LoggerFactory {
public:
std::unique_ptr<Logger> createLogger() override {
return std::make_unique<ConsoleLogger>();
}
};
// 使用
void process(LoggerFactory& factory) {
auto logger = factory.createLogger();
logger->log("processing...");
}
优点: 符合开闭原则,新增产品只需新增工厂类。 缺点: 类的数量翻倍(每个产品一个工厂)。
2. 抽象工厂模式
抽象工厂创建一组相关产品(产品族),保证产品配套。例如 UI 工厂同时创建按钮和文本框,Windows 工厂创建 Windows 风格的组件,Mac 工厂创建 Mac 风格的组件。
// 抽象工厂接口
class UIFactory {
public:
virtual std::unique_ptr<Button> createButton() = 0;
virtual std::unique_ptr<TextBox> createTextBox() = 0;
virtual ~UIFactory() = default;
};
// 具体工厂保证创建的产品是配套的
class WinFactory : public UIFactory { /* 创建 Win 风格组件 */ };
class MacFactory : public UIFactory { /* 创建 Mac 风格组件 */ };
3. 注册式工厂(实际工程常用)
class ShapeFactory {
public:
using Creator = std::function<std::unique_ptr<Shape>()>;
// 注册产品
static void registerShape(const std::string& name, Creator creator) {
getRegistry()[name] = std::move(creator);
}
// 创建产品
static std::unique_ptr<Shape> create(const std::string& name) {
auto it = getRegistry().find(name);
if (it != getRegistry().end()) {
return it->second();
}
return nullptr;
}
private:
static std::unordered_map<std::string, Creator>& getRegistry() {
static std::unordered_map<std::string, Creator> registry;
return registry;
}
};
// 注册(可以在各自的 .cpp 文件中)
static bool _ = [] {
ShapeFactory::registerShape("circle", [] {
return std::make_unique<Circle>();
});
return true;
}();
优点:
- 完全符合开闭原则
- 新增产品不需要修改工厂代码
- 支持动态注册(插件系统)
4. 配合智能指针的最佳实践
// ✅ 工厂返回 unique_ptr(推荐)
class Factory {
public:
static std::unique_ptr<Base> create(const std::string& type);
};
// 调用者可以选择所有权模型
auto obj = Factory::create("type"); // 独占
std::shared_ptr<Base> shared = Factory::create("type"); // 转为共享
// ❌ 工厂返回裸指针(不推荐)
// 调用者容易忘记 delete
为什么返回 unique_ptr:
- 明确所有权转移
- 调用者可以转为 shared_ptr(反过来不行)
- 异常安全
- 零开销(unique_ptr 和裸指针一样大)
5. 工厂模式 vs Builder 模式
| 对比项 | 工厂模式 | Builder 模式 |
|---|---|---|
| 关注点 | 创建哪种产品 | 怎么一步步构建产品 |
| 产品复杂度 | 简单对象 | 复杂对象(多个部件) |
| 创建过程 | 一步完成 | 分步骤构建 |
| 典型场景 | 根据类型创建不同对象 | 构建有很多可选参数的对象 |
6. 实际工程中的工厂应用
易错点
- 混淆三种工厂模式——简单工厂是一个类内 if-else,工厂方法是多个工厂类,抽象工厂是创建产品族。
- 说”工厂模式就是用来替代 new 的”——不是所有 new 都需要工厂,简单场景直接 new 更清晰。
- 忘记返回智能指针——C++ 中工厂返回裸指针是不好的实践。
- 过度使用工厂模式——只有一种产品、不需要扩展时,工厂模式是过度设计。
- 不知道注册式工厂——这是实际工程中最常用的变体。
- 说不出工厂模式的适用场景——要能举出具体例子(数据库驱动、日志后端等)。
记忆技巧
- 工厂核心思想:创建和使用分离,调用者不依赖具体类
- 三种工厂递进:简单(if-else)→ 方法(多工厂类)→ 抽象(产品族)
- 简单工厂缺点:违反开闭原则(要改 if-else)
- 返回类型口诀:工厂返回 unique_ptr,调用者决定所有权
- 什么时候用:类型由运行时决定、创建逻辑复杂、需要解耦
面试速答版
工厂模式将对象的创建和使用分离。简单工厂用一个类根据参数创建不同产品,缺点是违反开闭原则。工厂方法为每个产品定义一个工厂类,新增产品只需新增工厂。抽象工厂创建一组相关产品(产品族),保证产品配套。C++ 中工厂应返回 unique_ptr,明确所有权转移且异常安全。实际工程常用注册式工厂(map + function),支持动态注册新产品。适用场景:类型由运行时决定、创建逻辑复杂、需要解耦调用者和具体类。
Related · 设计模式