设计模式(C++)详解—工厂方法模式(2)
<摘要>
工厂方法模式就像一个万能玩具工厂,爸爸(抽象工厂)定义了制作玩具的标准流程,但让儿子们(具体工厂)决定具体生产哪种玩具。这种模式解决了"既要规范生产流程,又要灵活适应变化"的矛盾,通过将对象创建延迟到子类实现,让系统扩展像搭积木一样简单自然。本文将通过玩具工厂、披萨店和游戏武器三个生动故事,完整展示工厂方法模式在C++中的实现和应用。
<解析>
工厂方法模式:万能玩具工厂的故事
1. 背景故事:爸爸的玩具工厂
1.1 从前有个玩具大师
想象一下,有一位名叫老李的玩具大师,他开了家玩具工厂。最初,老李只生产一种玩具——木头小汽车。他的工厂是这样运作的:
// 简单工厂模式 - 老李的初期工厂
class Toy {
public:virtual void play() = 0;
};class WoodenCar : public Toy {
public:void play() override {std::cout << "嘟嘟!木头小汽车跑起来了!" << std::endl;}
};class SimpleToyFactory {
public:static Toy* createToy(const std::string& type) {if (type == "car") {return new WoodenCar();}// 每增加一种新玩具,就要修改这里return nullptr;}
};
老李发现一个问题:每次他想生产新玩具(比如塑料机器人),都要亲自修改工厂的核心代码。这就像每次想做新菜都要重建厨房一样麻烦!
1.2 儿子的灵感
老李有三个儿子,各自擅长制作不同类型的玩具:
- 大儿子擅长塑料玩具
- 二儿子擅长电子玩具
- 三儿子擅长毛绒玩具
老李想出了一个绝妙主意:“我不亲自做玩具了,我只制定做玩具的标准流程,让儿子们自己去决定具体做什么玩具!”
这就是工厂方法模式的精髓:父亲定义框架,儿子实现细节。
2. 核心概念:万能工厂的蓝图
2.1 工厂方法模式的童话解读
想象工厂方法模式就像一个乐高积木说明书:
- 抽象产品(Toy):说明书上说"这里要放一个可以玩的部件"
- 具体产品(WoodenCar/PlasticRobot):实际拿到的积木块
- 抽象工厂(ToyFactory):说明书上的组装步骤
- 具体工厂(PlasticToyFactory):你按照说明书实际组装的过程
2.2 三个关键角色
- 玩具大师(Client):想要玩具的孩子,不关心玩具怎么来的
- 工厂说明书(Creator):定义"如何生产玩具"的流程
- 具体工厂(Concrete Creator):实际动手生产的工匠
3. 设计意图:为什么需要万能工厂
3.1 现实世界的烦恼
假设你开了一家披萨店,最初只有一种披萨:
// 糟糕的设计:直接创建具体对象
class Pizza {
public:virtual void prepare() = 0;
};class CheesePizza : public Pizza {
public:void prepare() override {std::cout << "准备奶酪披萨..." << std::endl;}
};// 在订单处理中直接创建
void orderPizza(const std::string& type) {Pizza* pizza = nullptr;if (type == "cheese") {pizza = new CheesePizza();}// 每增加一种披萨,就要修改这里pizza->prepare();delete pizza;
}
这样做的痛点:
- 🚫 开闭原则 violation:增加新披萨要修改现有代码
- 🚫 紧耦合:订单系统依赖具体披萨类
- 🚫 难以测试:无法轻松模拟披萨对象
3.2 工厂方法模式的救赎
// 抽象披萨工厂
class PizzaFactory {
public:virtual Pizza* createPizza() = 0;// 模板方法:定义标准制作流程void makePizza() {Pizza* pizza = createPizza(); // 工厂方法pizza->prepare();pizza->bake();pizza->cut();pizza->box();delete pizza;}
};// 具体工厂
class CheesePizzaFactory : public PizzaFactory {
public:Pizza* createPizza() override {return new CheesePizza();}
};class VeggiePizzaFactory : public PizzaFactory {
public:Pizza* createPizza() override {return new VeggiePizza();}
};
现在要增加新披萨,只需要"增加"而不是"修改":
// 新增海鲜披萨:只需要扩展,不需要修改
class SeafoodPizza : public Pizza {
public:void prepare() override {std::cout << "准备新鲜海鲜披萨..." << std::endl;}
};class SeafoodPizzaFactory : public PizzaFactory {
public:Pizza* createPizza() override {return new SeafoodPizza();}
};
4. 实例应用:三个生动故事
4.1 故事一:玩具王国的扩张
背景:老李的玩具工厂越来越成功,现在要生产三种玩具:塑料机器人、电子恐龙、毛绒泰迪熊。
实现:
#include <iostream>
#include <memory>
#include <string>// 抽象产品:玩具
class Toy {
public:virtual ~Toy() {}virtual void play() = 0;
};// 具体产品
class PlasticRobot : public Toy {
public:void play() override {std::cout << "🤖 塑料机器人:变身!发射激光!" << std::endl;}
};class ElectronicDinosaur : public Toy {
public:void play() override {std::cout << "🦖 电子恐龙:发出震撼的咆哮声!" << std::endl;}
};class TeddyBear : public Toy {
public:void play() override {std::cout << "🧸 毛绒泰迪:给你一个温暖的拥抱!" << std::endl;}
};// 抽象工厂
class ToyFactory {
public:virtual ~ToyFactory() {}virtual std::unique_ptr<Toy> createToy() = 0;// 模板方法:标准生产流程void produceToy() {std::cout << "=== 开始生产玩具 ===" << std::endl;auto toy = createToy();toy->play();std::cout << "=== 玩具生产完成 ===\n" << std::endl;}
};// 具体工厂
class RobotFactory : public ToyFactory {
public:std::unique_ptr<Toy> createToy() override {return std::make_unique<PlasticRobot>();}
};class DinosaurFactory : public ToyFactory {
public:std::unique_ptr<Toy> createToy() override {return std::make_unique<ElectronicDinosaur>();}
};class TeddyFactory : public ToyFactory {
public:std::unique_ptr<Toy> createToy() override {return std::make_unique<TeddyBear>();}
};
使用示例:
int main() {// 创建不同的玩具工厂RobotFactory robotFactory;DinosaurFactory dinoFactory;TeddyFactory teddyFactory;// 生产各种玩具std::cout << "🎪 欢迎来到万能玩具工厂!\n" << std::endl;robotFactory.produceToy(); // 生产机器人dinoFactory.produceToy(); // 生产恐龙teddyFactory.produceToy(); // 生产泰迪熊return 0;
}
输出结果:
🎪 欢迎来到万能玩具工厂!=== 开始生产玩具 ===
🤖 塑料机器人:变身!发射激光!
=== 玩具生产完成 ====== 开始生产玩具 ===
🦖 电子恐龙:发出震撼的咆哮声!
=== 玩具生产完成 ====== 开始生产玩具 ===
🧸 毛绒泰迪:给你一个温暖的拥抱!
=== 玩具生产完成 ===
4.2 故事二:披萨店的数字化转型
背景:披萨店要支持线上订单,不同地区的店铺提供不同风味的披萨。
#include <iostream>
#include <memory>
#include <string>// 抽象产品:披萨
class Pizza {
public:virtual ~Pizza() {}virtual void prepare() = 0;virtual void bake() {std::cout << "🔥 烘烤25分钟..." << std::endl;}virtual void cut() {std::cout << "🔪 切成8片..." << std::endl;}virtual void box() {std::cout << "📦 打包完成!" << std::endl;}
};// 具体产品
class NewYorkCheesePizza : public Pizza {
public:void prepare() override {std::cout << "🍕 纽约风味奶酪披萨:薄脆饼底,额外奶酪" << std::endl;}
};class ChicagoVeggiePizza : public Pizza {
public:void prepare() override {std::cout << "🍕 芝加哥风味素食披萨:厚实饼底,新鲜蔬菜" << std::endl;}void cut() override {std::cout << "🔪 切成方形片..." << std::endl;}
};class CaliforniaSeafoodPizza : public Pizza {
public:void prepare() override {std::cout << "🍕 加州风味海鲜披萨:全麦饼底,新鲜海鲜" << std::endl;}
};// 抽象工厂
class PizzaStore {
public:virtual ~PizzaStore() {}virtual std::unique_ptr<Pizza> createPizza(const std::string& type) = 0;std::unique_ptr<Pizza> orderPizza(const std::string& type) {std::cout << "\n👨🍳 接到订单:" << type << "披萨" << std::endl;auto pizza = createPizza(type);pizza->prepare();pizza->bake();pizza->cut();pizza->box();std::cout << "✅ 订单完成!\n" << std::endl;return pizza;}
};// 具体工厂
class NewYorkPizzaStore : public PizzaStore {
public:std::unique_ptr<Pizza> createPizza(const std::string& type) override {if (type == "cheese") {return std::make_unique<NewYorkCheesePizza>();}throw std::runtime_error("不支持的披萨类型");}
};class ChicagoPizzaStore : public PizzaStore {
public:std::unique_ptr<Pizza> createPizza(const std::string& type) override {if (type == "veggie") {return std::make_unique<ChicagoVeggiePizza>();}throw std::runtime_error("不支持的披萨类型");}
};
4.3 故事三:游戏武器工厂
背景:游戏中有不同种族,每个种族有独特的武器制造方式。
#include <iostream>
#include <memory>
#include <vector>// 抽象产品:武器
class Weapon {
public:virtual ~Weapon() {}virtual void attack() = 0;virtual std::string getName() = 0;
};// 具体产品
class HumanSword : public Weapon {
public:void attack() override {std::cout << "⚔️ 长剑挥砍!造成20点伤害" << std::endl;}std::string getName() override { return "人类长剑"; }
};class ElfBow : public Weapon {
public:void attack() override {std::cout << "🏹 精灵长弓射击!造成25点伤害" << std::endl;}std::string getName() override { return "精灵长弓"; }
};class OrcAxe : public Weapon {
public:void attack() override {std::cout << "🪓 兽人战斧猛劈!造成30点伤害" << std::endl;}std::string getName() override { return "兽人战斧"; }
};// 抽象工厂
class Blacksmith {
public:virtual ~Blacksmith() {}virtual std::unique_ptr<Weapon> forgeWeapon() = 0;void demonstrateWeapon() {auto weapon = forgeWeapon();std::cout << "打造完成:" << weapon->getName() << std::endl;weapon->attack();}
};// 具体工厂
class HumanBlacksmith : public Blacksmith {
public:std::unique_ptr<Weapon> forgeWeapon() override {std::cout << "👨🏭 人类铁匠打造中..." << std::endl;return std::make_unique<HumanSword>();}
};class ElfBlacksmith : public Blacksmith {
public:std::unique_ptr<Weapon> forgeWeapon() override {std::cout << "🧝♀️ 精灵工匠制作中..." << std::endl;return std::make_unique<ElfBow>();}
};class OrcBlacksmith : public Blacksmith {
public:std::unique_ptr<Weapon> forgeWeapon() override {std::cout << "👹 兽人铁匠锻造中..." << std::endl;return std::make_unique<OrcAxe>();}
};
5. 交互流程:披萨订单的旅程
让我们用时序图展示一个披萨订单的完整生命周期:
6. 最佳实践:智慧工厂的秘诀
6.1 使用智能指针管理资源
// 好的实践:使用unique_ptr自动管理内存
class ModernToyFactory {
public:virtual std::unique_ptr<Toy> createToy() = 0;void produceToy() {auto toy = createToy(); // 自动内存管理toy->play();// 不需要手动delete,unique_ptr会自动处理}
};
6.2 工厂方法的参数化
// 参数化工厂方法
class FlexiblePizzaStore : public PizzaStore {
public:std::unique_ptr<Pizza> createPizza(const std::string& type) override {if (type == "cheese") {return std::make_unique<NewYorkCheesePizza>();} else if (type == "veggie") {return std::make_unique<ChicagoVeggiePizza>();} else if (type == "seafood") {return std::make_unique<CaliforniaSeafoodPizza>();}throw std::runtime_error("未知的披萨类型: " + type);}
};
6.3 工厂方法的注册机制
// 高级技巧:使用注册表动态管理工厂
class ToyFactoryRegistry {
private:std::map<std::string, std::function<std::unique_ptr<Toy>()>> factories;public:void registerFactory(const std::string& name, std::function<std::unique_ptr<Toy>()> factory) {factories[name] = factory;}std::unique_ptr<Toy> createToy(const std::string& name) {if (factories.find(name) != factories.end()) {return factories[name]();}throw std::runtime_error("未知的玩具类型: " + name);}
};// 使用注册机制
ToyFactoryRegistry registry;
registry.registerFactory("robot", []() { return std::make_unique<PlasticRobot>(); });
registry.registerFactory("dinosaur", []() { return std::make_unique<ElectronicDinosaur>(); });auto toy = registry.createToy("robot"); // 创建机器人
7. 总结:工厂方法模式的智慧
工厂方法模式就像父亲教儿子做菜:
- 👨🍳 父亲(抽象工厂):提供菜谱和烹饪方法
- 👦 儿子(具体工厂):根据菜谱实际做菜
- 🍳 菜品(具体产品):最终的美味佳肴
核心优势:
- ✅ 开闭原则:增加新菜品不用修改父亲的菜谱
- ✅ 解耦合:父亲不需要知道儿子具体怎么做菜
- ✅ 可扩展:儿子可以自由创新,只要符合菜谱标准
- ✅ 易测试:可以单独测试每个儿子的厨艺
适用场景:
- 🎮 游戏中的不同种族/职业拥有独特的生产方式
- 🏪 电商平台的不同商户有不同的商品创建逻辑
- 🌐 跨平台应用需要为不同操作系统创建UI组件
- 🏭 制造业中不同生产线生产类似但不同的产品
通过这个生动的故事和实际代码示例,相信你已经深刻理解了工厂方法模式的精髓。记住:好的设计不是什么都自己做,而是定义好规则,让他人自由发挥!