当前位置: 首页 > news >正文

设计模式(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 工厂方法模式的童话解读

想象工厂方法模式就像一个乐高积木说明书

  1. 抽象产品(Toy):说明书上说"这里要放一个可以玩的部件"
  2. 具体产品(WoodenCar/PlasticRobot):实际拿到的积木块
  3. 抽象工厂(ToyFactory):说明书上的组装步骤
  4. 具体工厂(PlasticToyFactory):你按照说明书实际组装的过程
玩具大师
+制定标准()
«抽象»
玩具工厂
+创建玩具()
大儿子工厂
+创建玩具()
二儿子工厂
+创建玩具()
«接口»
玩具
+玩耍()
塑料玩具
电子玩具

2.2 三个关键角色

  1. 玩具大师(Client):想要玩具的孩子,不关心玩具怎么来的
  2. 工厂说明书(Creator):定义"如何生产玩具"的流程
  3. 具体工厂(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. 交互流程:披萨订单的旅程

让我们用时序图展示一个披萨订单的完整生命周期:

顾客纽约披萨店披萨工厂纽约奶酪披萨订购奶酪披萨createPizza("cheese")new NewYorkCheesePizza()返回披萨对象prepare()准备完成bake()烘烤完成cut()切割完成box()打包完成交付披萨顾客纽约披萨店披萨工厂纽约奶酪披萨

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组件
  • 🏭 制造业中不同生产线生产类似但不同的产品

通过这个生动的故事和实际代码示例,相信你已经深刻理解了工厂方法模式的精髓。记住:好的设计不是什么都自己做,而是定义好规则,让他人自由发挥!


文章转载自:

http://TMq8J0FO.srgbr.cn
http://oUkR5ASy.srgbr.cn
http://7lG3T42f.srgbr.cn
http://8v0S59lS.srgbr.cn
http://5HKcSmtt.srgbr.cn
http://7owLBMR7.srgbr.cn
http://etgeoN3W.srgbr.cn
http://o5iWhG5t.srgbr.cn
http://jlnlTs7t.srgbr.cn
http://z5xcxInX.srgbr.cn
http://tvQmyXu5.srgbr.cn
http://c4pqd4Ra.srgbr.cn
http://qYLkJItb.srgbr.cn
http://YO90tCQz.srgbr.cn
http://1QajzxPT.srgbr.cn
http://0AI0fHln.srgbr.cn
http://BCGQG07l.srgbr.cn
http://6OsiE96u.srgbr.cn
http://VhkXNnUA.srgbr.cn
http://56XPcT3B.srgbr.cn
http://0pLAEC7T.srgbr.cn
http://q9iuikhB.srgbr.cn
http://m09w3FY3.srgbr.cn
http://CCmY3KdP.srgbr.cn
http://F45QHgpk.srgbr.cn
http://nSy8YOqb.srgbr.cn
http://wbo3bo1g.srgbr.cn
http://5G2bQSe1.srgbr.cn
http://g0qxHUz8.srgbr.cn
http://sTzm9rmd.srgbr.cn
http://www.dtcms.com/a/378653.html

相关文章:

  • 自动化运维实践:SaaS系统Nginx配置文件自动化运维脚本详解
  • 3D模型快速混沌加密matlab完整代码
  • 敏捷实践指南(中文版):章节梳理/主要知识体系和知识点
  • 第一章 ELK Stack基础概念与架构
  • GraphQL RPC 与通用索引器公测介绍:为 Sui 带来更强大的数据层
  • ShardingSphere 分库分表技术实现与实战案例
  • Docker 部署 MongoDB:单节点与副本集的最佳实践
  • OCR 识别表现好坏离不开什么?
  • 阿里云ACA认证[特殊字符]阿里云ACP认证
  • 计算机网络实验00---环境准备
  • 【路由交换技术】基于eNSP的多子网路由互通实验:从配置到验证的全流程指南
  • 【Python】Python解决阿里云DataWorks导出数据1万条限制的问题
  • 【GMX v1实战】时序风险结算与资本成本:深度解析 GMX 永续合约的资金费率机制
  • axios报错解决:unsupported BodyInit type
  • CRMEB多门店 v3.3源码 无授权限制+PC端+uniapp前端
  • `epoll_event` 结构体解析
  • 《Vuejs设计与实现》第 15 章(编译器核心技术)中
  • C#GDI
  • 智慧工地:科技赋能建筑业高质量发展的新引擎
  • 腾讯云智能体开发平台
  • 多个 Excel 表格如何合并为对应 Sheet 数量的单独 Xlsx 文件
  • 前端-v-model原理
  • 格式刷+快捷键:Excel和WPS表格隔行填充颜色超方便
  • 链表基础与操作全解析
  • GitHub 热榜项目 - 日榜(2025-09-11)
  • 中山GEO哪家好?技术视角解析关键词选词
  • 从零到一上手 Protocol Buffers用 C# 打造可演进的通讯录
  • 当DDoS穿上马甲:CC攻击的本质
  • 【ThreeJs】【自带依赖】Three.js 自带依赖指南
  • STM32短按,长按,按键双击实现