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
英文地址,請注明出處:http://en.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眼鏡在手術…