
目录一、为什么需要工厂二、简单工厂Simple Factory结构实现简单工厂的优点与缺点三、工厂方法模式Factory Method结构实现工厂方法模式与开闭原则四、简单工厂 vs 工厂方法选择指南五、完整例子游戏角色创建六、常见误区误区1工厂方法就是简单工厂 switch误区2每个类都要配一个工厂误区3工厂一定是单例七、这一篇的收获一、为什么需要工厂先看一个没有工厂的例子cppclass 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_ptrLogger create(Type type) { switch (type) { case FILE: return make_uniqueFileLogger(); case DATABASE: return make_uniqueDatabaseLogger(); case CLOUD: return make_uniqueCloudLogger(); default: return nullptr; } } // 也可以根据字符串创建 static unique_ptrLogger create(const string type) { if (type file) return make_uniqueFileLogger(); if (type database) return make_uniqueDatabaseLogger(); if (type cloud) return make_uniqueCloudLogger(); 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_ptrLogger createLogger() 0; virtual ~LoggerFactory() default; }; // 具体工厂 A class FileLoggerFactory : public LoggerFactory { string filename; public: FileLoggerFactory(const string f app.log) : filename(f) {} unique_ptrLogger createLogger() override { return make_uniqueFileLogger(filename); } }; // 具体工厂 B class DatabaseLoggerFactory : public LoggerFactory { string connection; public: DatabaseLoggerFactory(const string conn localhost) : connection(conn) {} unique_ptrLogger createLogger() override { return make_uniqueDatabaseLogger(connection); } }; // 具体工厂 C class CloudLoggerFactory : public LoggerFactory { string endpoint; public: CloudLoggerFactory(const string url https://api.log.com) : endpoint(url) {} unique_ptrLogger createLogger() override { return make_uniqueCloudLogger(endpoint); } }; // 客户端代码 class Application { unique_ptrLoggerFactory factory; public: Application(unique_ptrLoggerFactory f) : factory(move(f)) {} void run() { auto logger factory-createLogger(); logger-log(应用程序启动); // 业务逻辑... logger-log(应用程序关闭); } }; int main() { // 通过配置文件或命令行决定使用哪个工厂 string config cloud; unique_ptrLoggerFactory factory; if (config file) { factory make_uniqueFileLoggerFactory(custom.log); } else if (config database) { factory make_uniqueDatabaseLoggerFactory(prod-db); } else { factory make_uniqueCloudLoggerFactory(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_ptrCharacter create(const string name) 0; virtual ~CharacterFactory() default; }; // 具体工厂 class WarriorFactory : public CharacterFactory { public: unique_ptrCharacter create(const string name) override { return make_uniqueWarrior(name); } }; class MageFactory : public CharacterFactory { public: unique_ptrCharacter create(const string name) override { return make_uniqueMage(name); } }; class ArcherFactory : public CharacterFactory { public: unique_ptrCharacter create(const string name) override { return make_uniqueArcher(name); } }; // 游戏系统 class Game { vectorunique_ptrCharacter 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……哪种是线程安全的C11 之后的最佳实践是什么下篇详解。