目录
[二、简单工厂(Simple Factory)](#二、简单工厂(Simple Factory))
[三、工厂方法模式(Factory Method)](#三、工厂方法模式(Factory Method))
[四、简单工厂 vs 工厂方法](#四、简单工厂 vs 工厂方法)
[误区1:工厂方法就是简单工厂 + switch](#误区1:工厂方法就是简单工厂 + switch)
一、为什么需要工厂?
先看一个没有工厂的例子:
cpp
class Logger {
public:
virtual void log(const string& msg) = 0;
virtual ~Logger() = default;
};
class FileLogger : public Logger {
public:
void log(const string& msg) override {
cout << "写入文件: " << msg << endl;
}
};
class DatabaseLogger : public Logger {
public:
void log(const string& msg) override {
cout << "写入数据库: " << msg << endl;
}
};
// 客户端代码
int main() {
string type = "file"; // 可能来自配置文件
Logger* logger;
if (type == "file") {
logger = new FileLogger(); // 直接 new
} else {
logger = new DatabaseLogger();
}
logger->log("Hello");
delete logger;
}
问题:
-
创建逻辑散落在客户端
-
新增
CloudLogger需要修改所有创建点 -
客户端需要知道所有具体类
工厂模式的目标:把"选择哪个类"和"如何创建"的职责集中到一个地方。
二、简单工厂(Simple Factory)
结构
text
┌─────────────┐ ┌─────────────────┐
│ Product │◄─────│ SimpleFactory │
│ (抽象接口) │ │ + create(type) │
└─────────────┘ └─────────────────┘
▲
│
┌──────────┐ ┌───────────┐ ┌───────────┐
│ ProductA │ │ ProductB │ │ ProductC │
└──────────┘ └───────────┘ └───────────┘
实现
cpp
#include <iostream>
#include <memory>
#include <string>
using namespace std;
// 产品接口
class Logger {
public:
virtual void log(const string& msg) = 0;
virtual ~Logger() = default;
};
// 具体产品 A
class FileLogger : public Logger {
string filename;
public:
FileLogger(const string& f = "app.log") : filename(f) {}
void log(const string& msg) override {
cout << "[File:" << filename << "] " << msg << endl;
}
};
// 具体产品 B
class DatabaseLogger : public Logger {
string connection;
public:
DatabaseLogger(const string& conn = "localhost") : connection(conn) {}
void log(const string& msg) override {
cout << "[DB:" << connection << "] " << msg << endl;
}
};
// 具体产品 C
class CloudLogger : public Logger {
string endpoint;
public:
CloudLogger(const string& url = "https://api.log.com") : endpoint(url) {}
void log(const string& msg) override {
cout << "[Cloud:" << endpoint << "] " << msg << endl;
}
};
// ========== 简单工厂 ==========
class LoggerFactory {
public:
enum Type { FILE, DATABASE, CLOUD };
static unique_ptr<Logger> create(Type type) {
switch (type) {
case FILE:
return make_unique<FileLogger>();
case DATABASE:
return make_unique<DatabaseLogger>();
case CLOUD:
return make_unique<CloudLogger>();
default:
return nullptr;
}
}
// 也可以根据字符串创建
static unique_ptr<Logger> create(const string& type) {
if (type == "file") return make_unique<FileLogger>();
if (type == "database") return make_unique<DatabaseLogger>();
if (type == "cloud") return make_unique<CloudLogger>();
return nullptr;
}
};
// 客户端代码
int main() {
auto logger1 = LoggerFactory::create(LoggerFactory::FILE);
logger1->log("用户登录");
auto logger2 = LoggerFactory::create("cloud");
logger2->log("系统启动");
return 0;
}
简单工厂的优点与缺点
| 优点 | 缺点 |
|---|---|
| 简单直观,容易理解 | 违背开闭原则:每加新产品都要改工厂类 |
| 集中管理创建逻辑 | 工厂类会越来越臃肿 |
| 客户端与具体类解耦 | 无法通过继承扩展 |
三、工厂方法模式(Factory Method)
结构
text
┌─────────────┐ ┌─────────────┐
│ Product │ │ Factory │
│ (抽象接口) │ │ + create() │
└─────────────┘ └─────────────┘
▲ ▲
│ │
┌──────────┐ ┌─────────────┐
│ ProductA │ │ FactoryA │
└──────────┘ │ + create() │
└─────────────┘
核心思想:把工厂也抽象化。每个具体产品对应一个具体工厂,新增产品只需新增工厂类,不需要修改已有代码。
实现
cpp
#include <iostream>
#include <memory>
#include <string>
using namespace std;
// ========== 产品接口 ==========
class Logger {
public:
virtual void log(const string& msg) = 0;
virtual ~Logger() = default;
};
// 具体产品
class FileLogger : public Logger {
string filename;
public:
FileLogger(const string& f = "app.log") : filename(f) {}
void log(const string& msg) override {
cout << "[File:" << filename << "] " << msg << endl;
}
};
class DatabaseLogger : public Logger {
string connection;
public:
DatabaseLogger(const string& conn = "localhost") : connection(conn) {}
void log(const string& msg) override {
cout << "[DB:" << connection << "] " << msg << endl;
}
};
class CloudLogger : public Logger {
string endpoint;
public:
CloudLogger(const string& url = "https://api.log.com") : endpoint(url) {}
void log(const string& msg) override {
cout << "[Cloud:" << endpoint << "] " << msg << endl;
}
};
// ========== 工厂接口 ==========
class LoggerFactory {
public:
virtual unique_ptr<Logger> createLogger() = 0;
virtual ~LoggerFactory() = default;
};
// 具体工厂 A
class FileLoggerFactory : public LoggerFactory {
string filename;
public:
FileLoggerFactory(const string& f = "app.log") : filename(f) {}
unique_ptr<Logger> createLogger() override {
return make_unique<FileLogger>(filename);
}
};
// 具体工厂 B
class DatabaseLoggerFactory : public LoggerFactory {
string connection;
public:
DatabaseLoggerFactory(const string& conn = "localhost") : connection(conn) {}
unique_ptr<Logger> createLogger() override {
return make_unique<DatabaseLogger>(connection);
}
};
// 具体工厂 C
class CloudLoggerFactory : public LoggerFactory {
string endpoint;
public:
CloudLoggerFactory(const string& url = "https://api.log.com") : endpoint(url) {}
unique_ptr<Logger> createLogger() override {
return make_unique<CloudLogger>(endpoint);
}
};
// ========== 客户端代码 ==========
class Application {
unique_ptr<LoggerFactory> factory;
public:
Application(unique_ptr<LoggerFactory> f) : factory(move(f)) {}
void run() {
auto logger = factory->createLogger();
logger->log("应用程序启动");
// 业务逻辑...
logger->log("应用程序关闭");
}
};
int main() {
// 通过配置文件或命令行决定使用哪个工厂
string config = "cloud";
unique_ptr<LoggerFactory> factory;
if (config == "file") {
factory = make_unique<FileLoggerFactory>("custom.log");
} else if (config == "database") {
factory = make_unique<DatabaseLoggerFactory>("prod-db");
} else {
factory = make_unique<CloudLoggerFactory>("https://mycloud/log");
}
Application app(move(factory));
app.run();
return 0;
}
工厂方法模式与开闭原则
text
现有产品:FileLogger, DatabaseLogger
新增产品:CloudLogger
需要修改的代码:
- 新增 CloudLogger 类
- 新增 CloudLoggerFactory 类
不需要修改的代码:
- Logger 接口 ✓
- LoggerFactory 接口 ✓
- Application 类 ✓
- FileLogger / DatabaseLogger / 其工厂 ✓
完全符合开闭原则:对扩展开放(加新类),对修改关闭(不改旧类)。
四、简单工厂 vs 工厂方法
| 维度 | 简单工厂 | 工厂方法 |
|---|---|---|
| 工厂数量 | 1 个 | 每个产品 1 个 |
| 开闭原则 | ❌ 违背(改工厂类) | ✅ 符合(加新工厂类) |
| 代码量 | 少 | 多(类数量翻倍) |
| 复杂度 | 低 | 中等 |
| 适用场景 | 产品种类稳定 | 产品种类经常增加 |
| 扩展性 | 差 | 好 |
选择指南
text
产品种类是否稳定?
├── 是 → 简单工厂(足够)
└── 否 → 工厂方法
├── 或者抽象工厂(下一篇)
五、完整例子:游戏角色创建
cpp
#include <iostream>
#include <memory>
#include <vector>
using namespace std;
// ========== 产品:角色 ==========
class Character {
protected:
string name;
int health;
public:
Character(const string& n, int hp) : name(n), health(hp) {}
virtual void attack() = 0;
virtual void display() const {
cout << name << " (HP:" << health << ")";
}
virtual ~Character() = default;
};
class Warrior : public Character {
public:
Warrior(const string& n) : Character(n, 150) {}
void attack() override {
cout << name << " 使用旋风斩!" << endl;
}
};
class Mage : public Character {
public:
Mage(const string& n) : Character(n, 80) {}
void attack() override {
cout << name << " 施放火球术!" << endl;
}
};
class Archer : public Character {
public:
Archer(const string& n) : Character(n, 100) {}
void attack() override {
cout << name << " 射出穿云箭!" << endl;
}
};
// ========== 工厂接口 ==========
class CharacterFactory {
public:
virtual unique_ptr<Character> create(const string& name) = 0;
virtual ~CharacterFactory() = default;
};
// 具体工厂
class WarriorFactory : public CharacterFactory {
public:
unique_ptr<Character> create(const string& name) override {
return make_unique<Warrior>(name);
}
};
class MageFactory : public CharacterFactory {
public:
unique_ptr<Character> create(const string& name) override {
return make_unique<Mage>(name);
}
};
class ArcherFactory : public CharacterFactory {
public:
unique_ptr<Character> create(const string& name) override {
return make_unique<Archer>(name);
}
};
// ========== 游戏系统 ==========
class Game {
vector<unique_ptr<Character>> team;
public:
void recruit(CharacterFactory& factory, const string& name) {
team.push_back(factory.create(name));
cout << "招募: ";
team.back()->display();
cout << endl;
}
void battle() {
cout << "\n=== 战斗开始 ===" << endl;
for (auto& c : team) {
c->attack();
}
}
};
int main() {
Game game;
WarriorFactory warriorFactory;
MageFactory mageFactory;
ArcherFactory archerFactory;
// 玩家选择职业
int choice;
cout << "选择职业: 1.战士 2.法师 3.弓箭手" << endl;
cin >> choice;
if (choice == 1) {
game.recruit(warriorFactory, "亚瑟");
} else if (choice == 2) {
game.recruit(mageFactory, "甘道夫");
} else {
game.recruit(archerFactory, "罗宾汉");
}
// 新增角色(直接使用工厂)
game.recruit(warriorFactory, "贝奥武夫");
game.recruit(mageFactory, "梅林");
game.battle();
// 扩展新职业 Paladin:
// 1. 添加 class Paladin : public Character
// 2. 添加 class PaladinFactory : public CharacterFactory
// 3. 不需要修改任何已有代码
return 0;
}
六、常见误区
误区1:工厂方法就是简单工厂 + switch
工厂方法的本质是多态 ,不是条件判断。简单工厂才用 switch。
误区2:每个类都要配一个工厂
只有创建逻辑复杂(需要参数计算、条件判断、资源管理等)时才需要工厂。简单的 make_unique 不需要封装。
误区3:工厂一定是单例
工厂可以是单例,但不是必须。每个工厂可以有状态(如上面的 FileLoggerFactory 可以持有文件名)。
七、这一篇的收获
你现在应该理解:
-
简单工厂:一个工厂类根据参数创建不同产品;简单但违背开闭原则
-
工厂方法:每个产品有自己的工厂类;符合开闭原则,但类数量翻倍
-
选择标准:产品稳定用简单工厂,产品频繁增加用工厂方法
-
核心价值 :将
new的职责集中到工厂,客户端面向接口编程
💡 小作业:用工厂方法模式实现一个
Transport体系(Truck、Ship、Airplane)。Logistics类使用TransportFactory创建运输工具。添加一个新的Train类型,验证不需要修改已有代码。
下一篇预告:第40篇《单例模式(Singleton)的多种C++实现》------全局唯一实例。饿汉式、懒汉式、双检锁、Meyers Singleton......哪种是线程安全的?C++11 之后的最佳实践是什么?下篇详解。