c++注意点(11)----设计模式(工厂方法)

创建型模式

工厂方法模式是一种创建型设计模式, 其在父类中提供一个创建对象的方法, 允许子类决定实例化对象的类型。

为什么需要工厂方法模式?

看一个 “没有工厂模式” 的痛点场景:

假设你在开发一个游戏,最初只有 “战士” 一种角色,代码可能这样写:

// 早期代码:直接在客户端创建对象
void startGame() {// 直接new具体角色Warrior* warrior = new Warrior(); warrior->attack();// ...
}

但随着游戏迭代,需要添加 “法师” 角色,你不得不修改客户端代码:

// 迭代后:客户端代码被迫修改
void startGame(std::string roleType) {Character* character;// 客户端需要知道所有具体角色的类型if (roleType == "warrior") {character = new Warrior();} else if (roleType == "mage") {  // 新增逻辑character = new Mage();}character->attack();// ...
}

继续迭代添加 “弓箭手”“刺客” 时,客户端代码会被反复修改,最终变得臃肿且脆弱。

工厂方法优点

1.解耦对象创建与使用

2.符合开闭原则

        新增产品时,只需添加对应的具体产品类(如Assassin)和具体工厂类(如AssassinFactory),无需修改现有工厂和客户端代码。例如要增加 “刺客” 角色,原有的战士、法师工厂代码完全不用动。

3.单一职责原则

        每个具体工厂只负责创建一种产品(如WarriorFactory只创建战士),职责清晰,避免了一个类承担过多创建逻辑。

4.支持多态创建

        客户端可以通过相同的接口(抽象工厂)创建不同类型的产品。

5.便于扩展和测试

示例

#include <iostream>
#include <string>
#include <memory>// 产品接口:游戏角色
class Character {
public:virtual ~Character() = default;virtual void attack() const = 0;   // 攻击方法virtual void defend() const = 0;  // 防御方法virtual std::string getName() const = 0;  // 获取角色名称
};// 具体产品1:战士(近战高防御)
class Warrior : public Character {
public:void attack() const override {std::cout << "战士挥舞大剑,进行猛烈劈砍!" << std::endl;}void defend() const override {std::cout << "战士举起盾牌,形成坚固防御!" << std::endl;}std::string getName() const override {return "钢铁战士";}
};// 具体产品2:法师(远程魔法攻击)
class Mage : public Character {
public:void attack() const override {std::cout << "法师吟唱咒语,释放火球术!" << std::endl;}void defend() const override {std::cout << "法师召唤魔法屏障,抵御伤害!" << std::endl;}std::string getName() const override {return "元素法师";}
};// 具体产品3:弓箭手(远程物理攻击)
class Archer : public Character {
public:void attack() const override {std::cout << "弓箭手拉满长弓,射出精准箭矢!" << std::endl;}void defend() const override {std::cout << "弓箭手快速后跳,闪避攻击!" << std::endl;}std::string getName() const override {return "精灵弓箭手";}
};// 抽象工厂:角色创建工厂
class CharacterFactory {
public:virtual ~CharacterFactory() = default;virtual std::unique_ptr<Character> createCharacter() = 0;  // 工厂方法:创建角色
};// 具体工厂1:战士工厂
class WarriorFactory : public CharacterFactory {
public:std::unique_ptr<Character> createCharacter() override {std::cout << "=== 战士工厂创建角色 ===" << std::endl;return std::make_unique<Warrior>();  // 创建战士}
};// 具体工厂2:法师工厂
class MageFactory : public CharacterFactory {
public:std::unique_ptr<Character> createCharacter() override {std::cout << "=== 法师工厂创建角色 ===" << std::endl;return std::make_unique<Mage>();  // 创建法师}
};// 具体工厂3:弓箭手工厂
class ArcherFactory : public CharacterFactory {
public:std::unique_ptr<Character> createCharacter() override {std::cout << "=== 弓箭手工厂创建角色 ===" << std::endl;return std::make_unique<Archer>();  // 创建弓箭手}
};// 客户端:游戏角色选择系统
class GameSystem {
public:// 根据选择的工厂创建角色并执行操作static void playWithCharacter(CharacterFactory& factory) {auto character = factory.createCharacter();  // 通过工厂获取角色std::cout << "创建了角色:" << character->getName() << std::endl;// 角色执行战斗操作character->attack();character->defend();std::cout << std::endl;}
};int main() {// 玩家选择战士WarriorFactory warriorFactory;GameSystem::playWithCharacter(warriorFactory);// 玩家选择法师MageFactory mageFactory;GameSystem::playWithCharacter(mageFactory);// 玩家选择弓箭手ArcherFactory archerFactory;GameSystem::playWithCharacter(archerFactory);// 如需添加新角色(如刺客),只需新增:// 1. Assassin类(实现Character接口)// 2. AssassinFactory类(实现CharacterFactory接口)// 无需修改现有代码,符合开闭原则return 0;
}

c语言的工厂方法模式

        我们在用keil5开发单片机程序时候,可以也学着这种方法去开发。这样结构清晰。具体就是将共有的部分抽离出来,做结构体。功能的实现封装函数指针,放入结构体中。这样子对于多模块的使用,以及后续拓展都有很好的便捷性。

给出示例

#include <stdio.h>
#include <stdlib.h>
#include <string.h>// -------------------------- 外设抽象产品 --------------------------
// 外设抽象接口(相当于抽象类)
typedef struct Peripheral {void (*enable)(struct Peripheral*);   // 使能设备void (*disable)(struct Peripheral*);  // 禁用设备void (*set_param)(struct Peripheral*, int param, int value);  // 设置参数void (*destroy)(struct Peripheral*);  // 销毁资源const char* name;                     // 设备名称// 可以添加通用属性,如设备地址、状态等int address;int status; // 0: 禁用, 1: 使能
} Peripheral;// -------------------------- 具体产品实现(电机) --------------------------
// 电机特有参数定义
#define MOTOR_PARAM_SPEED 1    // 速度参数
#define MOTOR_PARAM_DIRECTION 2 // 方向参数// 电机使能
static void motor_enable(Peripheral* dev) {dev->status = 1;printf("[%s] 已使能 (地址: 0x%02X) - 开始运转\n", dev->name, dev->address);
}// 电机禁用
static void motor_disable(Peripheral* dev) {dev->status = 0;printf("[%s] 已禁用 (地址: 0x%02X) - 停止运转\n", dev->name, dev->address);
}// 电机参数设置(速度0-100,方向0-1)
static void motor_set_param(Peripheral* dev, int param, int value) {if (dev->status != 1) {printf("[%s] 未使能,无法设置参数\n", dev->name);return;}switch(param) {case MOTOR_PARAM_SPEED:printf("[%s] 设置速度: %d%%\n", dev->name, value);break;case MOTOR_PARAM_DIRECTION:printf("[%s] 设置方向: %s\n", dev->name, value ? "正向" : "反向");break;default:printf("[%s] 未知参数: %d\n", dev->name, param);}
}// 电机资源销毁
static void motor_destroy(Peripheral* dev) {printf("[%s] 销毁资源 (地址: 0x%02X)\n", dev->name, dev->address);free(dev);
}// 创建电机设备
Peripheral* create_motor(int address) {Peripheral* motor = (Peripheral*)malloc(sizeof(Peripheral));if (!motor) return NULL;motor->name = "直流电机";motor->address = address;motor->status = 0; // 初始禁用状态motor->enable = motor_enable;motor->disable = motor_disable;motor->set_param = motor_set_param;motor->destroy = motor_destroy;return motor;
}// -------------------------- 具体产品实现(显示屏) --------------------------
// 显示屏特有参数定义
#define DISPLAY_PARAM_BRIGHTNESS 1  // 亮度参数
#define DISPLAY_PARAM_CONTRAST 2    // 对比度参数// 显示屏使能
static void display_enable(Peripheral* dev) {dev->status = 1;printf("[%s] 已使能 (地址: 0x%02X) - 屏幕点亮\n", dev->name, dev->address);
}// 显示屏禁用
static void display_disable(Peripheral* dev) {dev->status = 0;printf("[%s] 已禁用 (地址: 0x%02X) - 屏幕关闭\n", dev->name, dev->address);
}// 显示屏参数设置(亮度0-100,对比度0-100)
static void display_set_param(Peripheral* dev, int param, int value) {if (dev->status != 1) {printf("[%s] 未使能,无法设置参数\n", dev->name);return;}switch(param) {case DISPLAY_PARAM_BRIGHTNESS:printf("[%s] 设置亮度: %d%%\n", dev->name, value);break;case DISPLAY_PARAM_CONTRAST:printf("[%s] 设置对比度: %d%%\n", dev->name, value);break;default:printf("[%s] 未知参数: %d\n", dev->name, param);}
}// 显示屏资源销毁
static void display_destroy(Peripheral* dev) {printf("[%s] 销毁资源 (地址: 0x%02X)\n", dev->name, dev->address);free(dev);
}// 创建显示屏设备
Peripheral* create_display(int address) {Peripheral* display = (Peripheral*)malloc(sizeof(Peripheral));if (!display) return NULL;display->name = "OLED显示屏";display->address = address;display->status = 0; // 初始禁用状态display->enable = display_enable;display->disable = display_disable;display->set_param = display_set_param;display->destroy = display_destroy;return display;
}// -------------------------- 具体产品实现(继电器) --------------------------
// 继电器特有参数定义
#define RELAY_PARAM_DELAY 1  // 延迟参数(毫秒)// 继电器使能
static void relay_enable(Peripheral* dev) {dev->status = 1;printf("[%s] 已使能 (地址: 0x%02X) - 开关闭合\n", dev->name, dev->address);
}// 继电器禁用
static void relay_disable(Peripheral* dev) {dev->status = 0;printf("[%s] 已禁用 (地址: 0x%02X) - 开关断开\n", dev->name, dev->address);
}// 继电器参数设置(延迟时间)
static void relay_set_param(Peripheral* dev, int param, int value) {if (dev->status != 1) {printf("[%s] 未使能,无法设置参数\n", dev->name);return;}switch(param) {case RELAY_PARAM_DELAY:printf("[%s] 设置延迟: %d毫秒\n", dev->name, value);break;default:printf("[%s] 未知参数: %d\n", dev->name, param);}
}// 继电器资源销毁
static void relay_destroy(Peripheral* dev) {printf("[%s] 销毁资源 (地址: 0x%02X)\n", dev->name, dev->address);free(dev);
}// 创建继电器设备
Peripheral* create_relay(int address) {Peripheral* relay = (Peripheral*)malloc(sizeof(Peripheral));if (!relay) return NULL;relay->name = "电磁继电器";relay->address = address;relay->status = 0; // 初始禁用状态relay->enable = relay_enable;relay->disable = relay_disable;relay->set_param = relay_set_param;relay->destroy = relay_destroy;return relay;
}// -------------------------- 外设工厂定义 --------------------------
// 外设类型枚举
typedef enum {PERIPHERAL_MOTOR,    // 电机PERIPHERAL_DISPLAY,  // 显示屏PERIPHERAL_RELAY     // 继电器
} PeripheralType;// 外设工厂:根据类型和地址创建对应外设
Peripheral* peripheral_factory_create(PeripheralType type, int address) {switch(type) {case PERIPHERAL_MOTOR:return create_motor(address);case PERIPHERAL_DISPLAY:return create_display(address);case PERIPHERAL_RELAY:return create_relay(address);default:printf("未知外设类型: %d\n", type);return NULL;}
}// -------------------------- 客户端示例(工业控制逻辑) --------------------------
// 外设控制通用函数(体现多态性)
void peripheral_operate(Peripheral* dev) {// 使能设备dev->enable(dev);// 设置设备特定参数(不同设备参数含义不同,但调用方式统一)if (strcmp(dev->name, "直流电机") == 0) {dev->set_param(dev, MOTOR_PARAM_SPEED, 70);dev->set_param(dev, MOTOR_PARAM_DIRECTION, 1);} else if (strcmp(dev->name, "OLED显示屏") == 0) {dev->set_param(dev, DISPLAY_PARAM_BRIGHTNESS, 80);dev->set_param(dev, DISPLAY_PARAM_CONTRAST, 50);} else if (strcmp(dev->name, "电磁继电器") == 0) {dev->set_param(dev, RELAY_PARAM_DELAY, 100);}// 禁用设备dev->disable(dev);
}int main() {// 通过工厂创建不同外设(地址用于区分硬件接口)Peripheral* motor = peripheral_factory_create(PERIPHERAL_MOTOR, 0x01);Peripheral* display = peripheral_factory_create(PERIPHERAL_DISPLAY, 0x02);Peripheral* relay = peripheral_factory_create(PERIPHERAL_RELAY, 0x03);if (!motor || !display || !relay) {printf("设备创建失败\n");return -1;}// 统一操作不同外设printf("=== 电机操作 ===\n");peripheral_operate(motor);printf("\n=== 显示屏操作 ===\n");peripheral_operate(display);printf("\n=== 继电器操作 ===\n");peripheral_operate(relay);// 销毁资源motor->destroy(motor);display->destroy(display);relay->destroy(relay);return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/90469.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/90469.shtml

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

相关文章

基于Kubernetes的微服务CI/CD:Jenkins Pipeline全流程实践

一、部署gitlab GitLab 是一个集代码托管、CI/CD、项目管理、安全扫描于一体的 DevOps 平台&#xff0c;提供从代码编写到部署的全生命周期管理。它支持 Git 版本控制&#xff0c;内置自动化流水线&#xff0c;可与 Kubernetes 集成&#xff0c;实现云原生应用的持续交付。同时…

Spring Bean初始化及@PostConstruc执行顺序

目录 1. Bean初始化执行顺序 2. 成员变量初始化顺序 2.1 普通Java类&#xff08;非Spring环境&#xff09; (1) 默认初始化(即初始分配内存) (2) 显式初始化 (3) 构造器初始化 (4)完整顺序 2.2 Spring管理的Bean&#xff08;依赖注入场景&#xff09; (1) 普通成员变量…

webRTC合并本地源码修改和官方更新

一、总体思路&#xff1a;基于 Git 分支管理改动origin/main 是官方 WebRTC 主干&#xff08;来自 webrtc.googlesource.com&#xff09;。my/webrtc 是你自己开发和修改的分支。每次 Google 更新 WebRTC&#xff0c;你从 origin/main 拉新代码&#xff0c;再把 my/webrtc 分支…

c++注意点(12)----设计模式(生成器)

创建型模式生成器模式&#xff08;Builder Pattern&#xff09;是一种创建型设计模式&#xff0c;它专注于将复杂对象的构建过程与表示分离&#xff0c;使得同样的构建过程可以创建不同的表示。就像是做饭&#xff0c;你可以自己慢慢做&#xff0c;各个步骤自己选择。而使用生成…

[特殊字符] VLA 如何“绕过”手眼标定?—— 当机器人学会了“看一眼就动手”

&#x1f52e; VLA 如何“绕过”手眼标定&#xff1f;—— 当机器人学会了“看一眼就动手” 作者&#xff1a;石去皿 发布时间&#xff1a;2025年7月 在传统机器人系统中&#xff0c;“手眼标定”是每一个工程师都绕不开的课题。 你必须精确测量相机和机械臂之间的空间变换关系…

《Maven 核心基础笔记(第一天)》

1.说明maven软件依赖管理和项目构建功能maven是为Java项目工作的 功能体现&#xff1a;依赖管理&#xff0c;项目构建 依赖管理&#xff1a;我们只需要写配置文件(pom.xml)&#xff0c;maven就会帮我们下载依赖&#xff0c;并且也会下载依赖的依赖。 项目构建&#xff1a;项目源…

Yolo底层原理学习(V1~V3)(第一篇)

一&#xff0c;卷积后的特征图大小计算众所周知&#xff0c;提到深度学习&#xff0c;必不可少的会提及卷积&#xff0c;那么如何计算卷积之后的图片大小呢&#xff1f;下图呈现&#xff1a;如图&#xff0c; 我们令FH&#xff0c;FW为原图像的长度FH*FW。P为padding的长度&…

前端开发项目性能瓶颈分析

1. 使用 rollup-plugin-visualizer 分析构建 借助 rollup-plugin-visualizer 插件&#xff0c;可以分析通过 rollup 构建出的产物内容&#xff0c;并生成可视化图表&#xff0c;帮助你分析打包后的文件大小以及各个模块的占用情况。 1.1. 安装插件 你需要在你的项目中安装 r…

ExoData.h - OpenExo

ExoData.h文件定位源代码1. 头文件依赖2. 核心类声明3. 主要成员函数关节遍历工具关节与配置相关数据/状态操作控制参数/校准4. 主要成员变量总结文件定位 位置&#xff1a;src/ExoData.h 作用&#xff1a;定义 ExoData 类&#xff0c;作为 Exo 系统全局数据的核心容器。它将设…

缓存HDC内容用于后续Direct2D绘制.

思路&#xff1a;把HDC里的内容保存到Direct2D格式的位图里&#xff0c;后续直接调用 renderTarget->DrawBitmap即可。本例中&#xff0c;位图将保存为类的字段。本例中 COM 接口指针皆使用 com_ptr&#xff0c;这是 WinRT 的 COM 智能指针类&#xff0c;com_ptr<I>::…

“抓了个寂寞”:一次实时信息采集的意外和修复

1. 那天下午&#xff0c;舆情系统“迟到”了 那天下午&#xff0c;公司运营那边突然在群里喊&#xff1a;“XX事件都快上热搜榜前十了&#xff0c;咱们系统咋没反应&#xff1f;” 我愣了几秒&#xff0c;立马翻后台日志、爬虫执行记录&#xff0c;结果一查&#xff0c;还真有点…

数据结构之迪杰斯特拉算法

前言&#xff1a;前面两篇文章介绍了生成图的最小生成树的算法&#xff0c;接下来两篇文章会介绍图的最短路径的算法&#xff0c;迪杰斯特拉算法和弗洛伊德算法。迪杰斯特拉算法是用来计算一个点到其他所有点的最短路径&#xff0c;这个点称之为源点。 一、实现流程 回忆一下…

技术文档 | OpenAI 的 Kafka 演进之路与 Pulsar 迁移潜力

导读ChatGPT 用户量指数级暴涨&#xff0c;OpenAI 的 Kafka 集群在一年内增长 20 倍至 30 个集群[1]&#xff0c;其 Kafka 架构面临日均千亿级消息&#xff08;峰值 QPS 800万/秒&#xff09; 的压力。这揭示了一个关键事实&#xff1a;OpenAI 的成功不只依赖模型&#xff0c;更…

【bug】 jetson上opencv无法录制h264本地视频

在Jetson Orin NX上无法使用opencv直接录制h264/h265视频流&#xff08;h264格式的视频流才能在浏览器播放&#xff09; 解决&#xff1a; 软件编码&#xff1a;需要源码编译opencv 1.环境准备 pip uninstall opencv-python sudo apt install build-essential cmake git python…

解决http的web服务中与https服务交互的问题

问题背景&#xff1a; 需要在一个http的web服务中直接跟另一个https服务交互&#xff0c;不经过自身后端。 又来到了熟悉的跨域访问问题。 解决逻辑就是使用nginx转发&#xff0c;涉及到的文件也就是nginx.conf文件&#xff0c;前面解决minio链接时已经有经验了&#xff0c;但…

网站访问信息追踪系统在安全与性能优化中的关键作用——网络安全—仙盟创梦IDE

<?php // 收集访问信息 $visitorInfo未来之窗 [timestamp > date(Y-m-d H:i:s),ip > $_SERVER[REMOTE_ADDR] ?? unknown,page > $_SERVER[REQUEST_URI] ?? unknown,method > $_SERVER[REQUEST_METHOD] ?? unknown,user_agent > $_SERVER[HTTP_USER_A…

Oracle 时间处理函数和操作符笔记

前言 写sql时经常用到时间处理函数&#xff0c;我整理了一份Oracle的常用sql笔记,供大家参考。 如果对你有帮助&#xff0c;请点赞支持~ 多谢&#x1f64f; 笔记 -- 1. 获取当前日期和时间 -- SYSDATE, SYSTIMESTAMP, CURRENT_DATE, CURRENT_TIMESTAMP, LOCALTIMESTAMP SELE…

TDengine时序数据库 详解

1. TDengine 简介 TDengine 是一款 高性能、分布式、支持 SQL 的时序数据库&#xff08;Time-Series Database, TSDB&#xff09;&#xff0c;专为 物联网&#xff08;IoT&#xff09;、工业互联网、金融监控、日志分析 等场景设计。其核心特点包括&#xff1a; 超高性能&…

【IDEA】idea怎么修改注册的用户名称?

文章目录[toc]问题**方法 1&#xff1a;通过 JetBrains 账户网站修改****方法 2&#xff1a;通过 IDEA 内跳转修改&#xff08;快捷方式&#xff09;****注意事项****补充&#xff1a;修改 IDEA 内的项目级用户名**如何退出IDEA用户登录&#xff1f;问题 在 IntelliJ IDEA 中修…

AR眼镜重塑外科手术导航:精准“透视”新突破

在现代医学领域&#xff0c;增强现实&#xff08;AR www.teamhelper.cn &#xff09;技术正以前所未有的方式改变外科手术导航的面貌。通过为医生提供实时的三维可视化、精准的空间定位和智能交互功能&#xff0c;AR眼镜正在成为手术室中的重要工具。本文将系统介绍AR眼镜在手术…