设计模式(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://www.pswp.cn/pingmian/96446.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/96446.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

187. Java 异常 - 什么是异常?

文章目录187. Java 异常 - 什么是异常&#xff1f;&#x1f6a8; 什么是“异常”&#xff1f;✅ 定义&#xff1a;&#x1f9f1; 异常对象中包含什么&#xff1f;Java 是怎么“处理”异常的&#xff1f;&#x1f9ef; 什么是异常处理器&#xff08;Exception Handler&#xff0…

3D Tiles 工具

概述 3D Tiles 工具是一组用于转换、优化、处理和分析 3D Tiles 数据的工具和实用程序。 安装 要在本地目录中安装 3D Tiles 工具&#xff0c;请运行以下命令&#xff1a; npm install 3d-tiles-tools如果想直接使用 Git 仓库的克隆版本&#xff0c;请参阅开发者设置。 命…

【编号520】全国4500多个地震灾害点位数据(2021.2-2025.8)

今天小编整理分享的是 全国4500多个地震灾害点位数据&#xff08;2021.2-2025.8)。概况数据概况全国4500多个地震灾害点位数据&#xff08;2021.2-2025.8&#xff09;地质灾害点位数据-地震&#xff01;数据含发生时间、地点、经纬坐标、灾害规模等。数据为shp格式和excel表格…

DriftingBlues: 4靶场渗透

DriftingBlues: 4 来自 <https://www.vulnhub.com/entry/driftingblues-4,661/> 1&#xff0c;将两台虚拟机网络连接都改为NAT模式 2&#xff0c;攻击机上做namp局域网扫描发现靶机 nmap -sn 192.168.23.0/24 那么攻击机IP为192.168.23.128&#xff0c;靶场IP192.168.23…

GEO 优化专家孟庆涛以 AI 技术建体系,赋能多行业智能化转型

在生成式 AI 重塑全球搜索生态的浪潮中&#xff0c;中国 GEO&#xff08;生成式引擎优化&#xff09;领域的开拓者孟庆涛以 "智能决策革命" 的技术框架&#xff0c;颠覆了传统 "发发文章" 的简单认知。作为辽宁粤穗网络科技有限公司总经理兼 GEO 实验室主任…

架构很简单:从架构的角度学习源码

缘起最近出差比较多&#xff0c;在路上思考&#xff1a;如何学习源码&#xff1f;是的&#xff0c;面试官问你看了哪些源码&#xff1f;讲一讲&#xff1f;更高级的&#xff1a;说一下&#xfeff;Netty黏包拆包怎么实现的&#xff1f;或者再问的偏一点&#xff1f;讲一下某个功…

R的安装与使用

工作与学习需要&#xff0c;对R需要有一定的熟悉&#xff0c;特此记录 目录R的安装与使用R的安装R的安装与使用 R的安装 1.找到R的官网&#xff0c;百度直接搜索&#xff0c;或者www.bing.com国际版搜索即可&#xff0c;下载R与Rstudio&#xff0c;Rstudio是R的开发集成工具 …

界面规范10-树

字体字号和其他地方一致&#xff1a;.el-tree-node__label{font-size: $defaultTxtSize;font-family: $defaultFontFamily;}选中效果&#xff1a;.el-tree-node.is-current>.el-tree-node__content {background-color: #ffffff !important;color: #0000ff !important;font-w…

Win 11 ARM 版搭建ESP-IDF环境问题记录

整理我在ARM版 WIN 11 上配置ESP-IDF 环境的记录 WIN 11环境下搭建ESP-IDF 命令环境&#xff0c;有几种办法&#xff1a; 下载ESP-IDF 安装程序 从https://dl.espressif.com/dl/esp-idf/ 下载安装程序&#xff0c;下载的程序只能在AMD64位CPU上&#xff0c;不能在ARM 芯片上使用…

FreeBSD系统使用freebsd-update命令从14.2升级到14.3

FreeBSD系统使用freebsd-update命令从14.2升级到14.3 升级操作 先升级小版本命令 # 检查系统漏洞补丁 sudo freebsd-update fetch sudo freebsd-update install再升级到14.3 sudo freebsd-update upgrade -r 14.3-RELEASE 速度很慢啊 执行install # freebsd-update inst…

一键拖动去除文件夹及文件名称的空格

经常会碰见文件夹&#xff0c;以及其中文件命名有空格的情况&#xff1a;这些空格对有些代码程序不友好&#xff0c;做了一个一键拖动去除文件名称空格 只用把文件夹或者多个文件拖动到bat代码上就可以一键搞定 分为两个文件&#xff0c;需要放在一个目录下&#xff1a; replac…

Roo Code:用自然语言编程的VS Code扩展

在编程的世界里&#xff0c;常常需要记忆各种复杂的命令和语法&#xff0c;与机器进行“对话”。但 如果使用Roo Code&#xff0c;就可以让编程交流变得像日常对话一样简单自然。用自然语言“打字”交流 Roo Code的核心设计理念是理解自然语言。这意味着&#xff0c;无需掌握任…

window显示驱动开发—视频呈现网络简介

视频呈现网络 (VidPN) 管理器是 DirectX 图形内核子系统 (Dxgkrnl.sys) 的组件&#xff0c;负责管理连接到显示适配器的监视器和其他显示设备的集合。 VidPN 经理的职责包括&#xff1a;响应热插拔和拔下监视器。当连接的监视器集更改时&#xff0c;维护和更新一组可用的显示模…

安卓13_ROM修改定制化-----打开摄像头调用相机功能 实现无人直播

无人直播功能的实现而修改手机固件,让打开摄像头时默认启动相机功能,需要理解安卓系统中摄像头调用的流程 ,而相机应用是通过绑定该服务实现功能的。 通过博文了解💝💝💝 1💝💝💝-----了解修改无人直播功能实现的核心原理 2💝💝💝-----修改rom 实现更改…

系统架构设计师——【2024年上半年案例题】真题模拟与解析(二)

系统架构设计师——【2024年上半年案例题】真题模拟与解析&#xff08;二&#xff09;试题三&#xff1a;微服务架构与缓存策略 题目背景&#xff1a; 某电商公司计划将其单体架构的系统重构为微服务架构&#xff0c;以应对日益增长的业务复杂性和用户并发请求。新系统划分为用…

加固笔记本是什么意思?加固笔记本图片

在现代信息化社会中&#xff0c;笔记本电脑已经成为人们学习、办公和娱乐的必备工具。然而&#xff0c;我们常见的笔记本大多用于相对稳定、舒适的环境中&#xff0c;强调的是轻薄、便携和性能。但在一些极端复杂的行业和场景中&#xff0c;普通笔记本往往难以胜任。比如沙漠勘…

CSS高级技巧---精灵图、字体图标、布局技巧

1、精灵图1&#xff09;原理核心原理&#xff1a;将网页中的一些小背景图像整合到一张大图中&#xff0c;这样服务器只需要一次请求就可以了。精灵技术的目的&#xff1a;为了有效减少服务器接收和发送请求的次数&#xff0c;提高页面的加载速度。2&#xff09;使用使用精灵图核…

【CMake】环境变量

目录 一.环境变量简单介绍 1.1.示例1——设置与清除 1.2.示例2——全局唯一性和全局可见性 1.3.示例3 1.4.示例4 1.5.示例5 一.环境变量简单介绍 什么是环境变量&#xff1f; 我们看看官网是怎么说环境变量的&#xff1a;cmake-language(7) — CMake 4.1.1 Documentation…

Mysql杂志(十四)——Mysql逻辑架构

Mysql逻辑架构 这个图相信大家并不陌生&#xff0c;这个就是mysql的逻辑结构图下面是主包让AI生成的翻译后的逻辑结构图&#xff1a; 我们先来看看各个部分的作用是什么 第一层&#xff1a;客户端连接层 (Client Layer) 这一层负责处理所有客户端连接到服务器的请求。责任就…

Git 版本回退与撤销修改

作为版本控制管理器&#xff0c;Git应当具备版本回退等一系列功能——它的应用场景也很常见&#xff0c;当你在工作区开发时&#xff0c;忽然发现&#xff1a;怎么我这版本写的代码还不如上一版本好&#xff1f;这时&#xff0c;版本回退功能就派上用场了。一.版本回退1.概览首…