【C++】組合模式

目錄

  • 一、模式核心概念與結構
  • 二、C++ 實現示例:文件系統
  • 三、組合模式的關鍵特性
  • 四、應用場景
  • 五、組合模式與其他設計模式的關系
  • 六、C++ 標準庫中的組合模式應用
  • 七、優缺點分析
  • 八、實戰案例:圖形編輯器
  • 九、實現注意事項
    • 如果這篇文章對你有所幫助,渴望獲得你的一個點贊!

組合模式(Composite Pattern)是一種【結構型】設計模式,它允許你將對象組合成樹形結構以表示 “部分 - 整體” 的層次關系。這種模式使得客戶端可以統一處理單個對象和對象組合,無需區分它們的具體類型。

一、模式核心概念與結構

組合模式包含三個核心角色:

  1. 組件(Component):定義組合中所有對象的通用接口,聲明管理子組件的方法。
  2. 葉節點(Leaf):表示組合中的葉節點對象,沒有子節點,實現組件接口。
  3. 組合節點(Composite):表示組合中的分支節點,包含子組件,實現組件接口并管理子組件。

二、C++ 實現示例:文件系統

以下是一個經典的組合模式示例,演示如何用組合模式表示文件系統:

#include <iostream>
#include <string>
#include <vector>
#include <memory>// 組件:文件系統元素
class FileSystemElement {
public:virtual ~FileSystemElement() = default;virtual void print(int depth = 0) const = 0;virtual size_t getSize() const = 0;virtual void add(std::shared_ptr<FileSystemElement> element) {}virtual void remove(std::shared_ptr<FileSystemElement> element) {}
};// 葉節點:文件
class File : public FileSystemElement {
private:std::string name;size_t size;public:File(const std::string& n, size_t s) : name(n), size(s) {}void print(int depth) const override {std::cout << std::string(depth * 2, ' ') << "- " << name << " (file, " << size << " bytes)" << std::endl;}size_t getSize() const override {return size;}
};// 組合節點:目錄
class Directory : public FileSystemElement {
private:std::string name;std::vector<std::shared_ptr<FileSystemElement>> children;public:Directory(const std::string& n) : name(n) {}void print(int depth) const override {std::cout << std::string(depth * 2, ' ') << "+ " << name << " (directory, " << getSize() << " bytes)" << std::endl;for (const auto& child : children) {child->print(depth + 1);}}size_t getSize() const override {size_t total = 0;for (const auto& child : children) {total += child->getSize();}return total;}void add(std::shared_ptr<FileSystemElement> element) override {children.push_back(element);}void remove(std::shared_ptr<FileSystemElement> element) override {for (auto it = children.begin(); it != children.end(); ++it) {if (*it == element) {children.erase(it);break;}}}
};// 客戶端代碼
int main() {// 創建目錄結構auto root = std::make_shared<Directory>("/");auto home = std::make_shared<Directory>("home");auto user = std::make_shared<Directory>("user");auto docs = std::make_shared<Directory>("documents");// 添加文件docs->add(std::make_shared<File>("report.txt", 1024));docs->add(std::make_shared<File>("presentation.pdf", 5120));user->add(docs);user->add(std::make_shared<File>("profile.jpg", 2048));home->add(user);root->add(home);root->add(std::make_shared<File>("readme.txt", 512));// 打印文件系統結構root->print();// 計算總大小std::cout << "\nTotal size: " << root->getSize() << " bytes" << std::endl;return 0;
}

三、組合模式的關鍵特性

  1. 統一接口
    • 組件接口定義了葉節點和組合節點的共同行為(如print()getSize())。
    • 客戶端可以一致地處理單個對象和對象組合。
  2. 遞歸結構
    • 組合節點可以包含其他組合節點或葉節點,形成樹形結構。
    • 操作可以遞歸地應用于整個樹結構。
  3. 透明性 vs 安全性
    • 透明性:在組件接口中聲明所有管理子節點的方法(如add()remove()),使葉節點和組合節點具有相同接口,但可能導致葉節點運行時錯誤。
    • 安全性:僅在組合節點中聲明管理子節點的方法,葉節點不包含這些方法,但客戶端需區分葉節點和組合節點。

四、應用場景

  1. 樹形結構表示
    • 文件系統、XML/JSON 解析樹。
    • 組織結構圖、菜單系統。
  2. 統一處理對象
    • 圖形編輯器中的形狀組合(如 Group、Layer)。
    • 游戲中的場景圖(Scene Graph)。
  3. 遞歸操作
    • 數學表達式計算(如加減乘除組合)。
    • 權限管理中的角色和權限組。

五、組合模式與其他設計模式的關系

  1. 迭代器模式
    • 組合模式常與迭代器模式結合,用于遍歷樹形結構。
    • 例如,使用迭代器遍歷文件系統中的所有文件。
  2. 訪問者模式
    • 組合模式可以配合訪問者模式,將算法與對象結構分離。
    • 例如,通過訪問者模式實現文件系統的大小統計、搜索等操作。
  3. 享元模式
    • 組合模式的葉節點可以是享元對象,共享內部狀態以節省內存。
    • 例如,文件系統中的相同文件可以共享同一個對象實例。

六、C++ 標準庫中的組合模式應用

  1. STL 容器
    • std::vectorstd::list等容器可以存儲不同類型的元素,形成樹形結構。
    • 例如,std::vector<std::shared_ptr<Component>>可以存儲組合節點和葉節點。
  2. 智能指針
    • std::shared_ptrstd::unique_ptr可用于管理組合結構中的對象生命周期。
    • 例如,在文件系統示例中使用std::shared_ptr避免內存泄漏。
  3. 流類庫
    • std::iostream層次結構中,std::iostream是抽象組件,std::ifstreamstd::ofstream是葉節點,std::stringstream可視為組合節點。

七、優缺點分析

優點:

  • 簡化客戶端代碼:客戶端無需區分處理葉節點和組合節點。
  • 靈活擴展:可以輕松添加新的葉節點或組合節點。
  • 樹形結構清晰:明確表示 “部分 - 整體” 的層次關系。

缺點:

  • 限制類型安全:透明實現可能導致運行時錯誤(如對葉節點調用add())。
  • 設計復雜度:在某些情況下,過度使用組合模式可能使設計變得復雜。
  • 性能問題:遞歸操作可能導致性能開銷,尤其是大型樹結構。

八、實戰案例:圖形編輯器

以下是一個圖形編輯器的組合模式實現:

#include <iostream>
#include <string>
#include <vector>
#include <memory>
#include <cmath>// 組件:圖形元素
class Shape {
public:virtual ~Shape() = default;virtual void draw() const = 0;virtual double area() const = 0;virtual void add(std::shared_ptr<Shape> shape) {}virtual void remove(std::shared_ptr<Shape> shape) {}
};// 葉節點:圓形
class Circle : public Shape {
private:double radius;std::string color;public:Circle(double r, const std::string& c) : radius(r), color(c) {}void draw() const override {std::cout << "Drawing Circle with radius " << radius << " and color " << color << std::endl;}double area() const override {return M_PI * radius * radius;}
};// 葉節點:矩形
class Rectangle : public Shape {
private:double width, height;std::string color;public:Rectangle(double w, double h, const std::string& c) : width(w), height(h), color(c) {}void draw() const override {std::cout << "Drawing Rectangle with width " << width << ", height " << height << " and color " << color << std::endl;}double area() const override {return width * height;}
};// 組合節點:圖形組
class Group : public Shape {
private:std::string name;std::vector<std::shared_ptr<Shape>> shapes;public:Group(const std::string& n) : name(n) {}void draw() const override {std::cout << "Group " << name << " contains:" << std::endl;for (const auto& shape : shapes) {shape->draw();}}double area() const override {double total = 0;for (const auto& shape : shapes) {total += shape->area();}return total;}void add(std::shared_ptr<Shape> shape) override {shapes.push_back(shape);}void remove(std::shared_ptr<Shape> shape) override {for (auto it = shapes.begin(); it != shapes.end(); ++it) {if (*it == shape) {shapes.erase(it);break;}}}
};// 客戶端代碼
int main() {// 創建圖形元素auto circle = std::make_shared<Circle>(5.0, "red");auto rectangle = std::make_shared<Rectangle>(4.0, 6.0, "blue");// 創建圖形組auto group1 = std::make_shared<Group>("Group 1");group1->add(circle);group1->add(rectangle);// 創建另一個圖形組并添加子組auto group2 = std::make_shared<Group>("Group 2");auto anotherCircle = std::make_shared<Circle>(3.0, "green");group2->add(anotherCircle);group2->add(group1);  // 添加子組// 繪制所有圖形group2->draw();// 計算總面積std::cout << "\nTotal area: " << group2->area() << std::endl;return 0;
}

九、實現注意事項

  1. 內存管理
    • 使用智能指針(如std::shared_ptr)管理組合結構中的對象生命周期。
    • 避免循環引用導致內存泄漏(可使用std::weak_ptr)。
  2. 接口設計
    • 根據需要選擇透明性(在基類中聲明所有方法)或安全性(僅在組合類中聲明特定方法)。
  3. 遞歸深度
    • 對于大型樹結構,遞歸操作可能導致棧溢出,考慮使用迭代或尾遞歸優化。
  4. 線程安全
    • 在多線程環境中,修改組合結構(如add()remove())需考慮同步問題。

組合模式是 C++ 中處理樹形結構的重要工具,通過統一接口和遞歸組合,使客戶端可以一致地處理單個對象和對象組合,從而簡化了代碼設計并提高了系統的可擴展性。


如果這篇文章對你有所幫助,渴望獲得你的一個點贊!

在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/86727.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/86727.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/86727.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

C++包管理工具:conan2持續集成 (CI) 教程

1.持續集成 (CI) ? 這是一個高級主題&#xff0c;需要具備 Conan 的基礎知識。請先閱讀并練習用戶教程。本節面向設計和實施涉及 Conan 包的生產 CI 管道的 DevOps 和構建工程師。如果不是這種情況&#xff0c;您可以跳過本節。 持續集成 (CI) 對不同用戶和組織有不同的含義…

免費SSL證書一鍵申請與自動續期

免費SSL證書申請與自動續期教程 本文介紹如何通過樂此加密&#xff08;www.letsencrypt.top) 實現免費SSL證書一鍵配置和自動續期 一、準備工作 服務器要求 Linux 系統&#xff08;推薦 Ubuntu/CentOS&#xff09;已安裝 curl 和 crontab擁有 sudo 權限的用戶 域名驗證 確保域…

【NLP】自然語言項目設計

目錄 項目簡介 要求 需要考慮的問題 硬件需求和環境配置 n卡驅動配置 以cuda11.8 版本為例 下載對應的cudnn(version11) 安裝GPU版本的torch 安裝gpu版本的TensorFlow 檢查cuda安裝情況 項目簡介 訓練一個模型&#xff0c;實現歌詞仿寫生成 任務類型&#xff1a;文本…

設計模式:觀察者模式 (Observer) 案例詳解

目錄 一、引言&#xff1a;為什么需要觀察者模式&#xff1f; 二、觀察者模式的核心原理 1. 角色劃分 2. 類圖關系 三、經典案例解析 案例1&#xff1a;天氣監測系統 案例2&#xff1a;股票價格監控系統 案例3&#xff1a;MVC架構中的模型-視圖分離 案例4&#xff1a;J…

CTF-Misc:開啟全方位解題之旅

目錄 一、CTF-Misc 入門指引二、基礎技能儲備2.1 文件格式識別2.2 基礎工具使用 三、信息搜集技巧3.1 搜索引擎技巧3.2 網絡信息挖掘 四、編碼轉換奧秘4.1 常見編碼類型4.2 編碼轉換工具 五、隱寫分析秘籍5.1 圖片隱寫5.1.1 LSB 隱寫5.1.2 顏色通道與 Exif 信息5.1.3 圖片修復與…

Adobe創意套件深度挖掘:效率倍增與靈感迸發的新玩法

最近在深入體驗奧地利Blueskyy藝術學院的Adobe正版教育訂閱&#xff0c;并研究全家桶時有不少新發現&#xff0c;忍不住想和大家分享一下。 先簡單說下這個訂閱的感受&#xff1a; Firefly 積分。 這應該是我用過Firefly積分最多的版本&#xff0c;1500點/周。對于我們這些創意…

左神算法之有序二維矩陣中的目標值查找

有序二維矩陣中的目標值查找 目錄 有序二維矩陣中的目標值查找1. 題目描述2. 問題解釋3. 解決思路方法一&#xff1a;逐行二分查找&#xff08;適合行數較少的情況&#xff09;方法二&#xff1a;利用行列有序特性&#xff08;最優解&#xff09; 4. 代碼實現5. 總結 1. 題目描…

深入理解AVL樹及其旋轉操作

AVL樹的概念 二叉搜索樹雖可以縮短查找的效率&#xff0c;但如果數據有序或接近有序二叉搜索樹將退化為單枝樹&#xff0c;查找元素相當于在順序表中搜索元素&#xff0c;效率低下。因此&#xff0c;兩位俄羅斯的數學家G.M.Adelson-Velskii和E.M.Landis在1962年發明了一種方法…

URL帶有中文會引入哪些問題

處理含中文字符的 URL 1 為什么會出現“亂碼”或崩潰&#xff1f; URL 標準&#xff08;RFC 3986&#xff09;規定&#xff1a;除少數保留字符外&#xff0c;URL 只能包含 ASCII。中文屬于 Unicode&#xff0c;因此必須先轉換。如果直接把 https://example.com/路徑/ 這樣的字…

結構體字段能否單獨加 mut

你問的這個問題在 Rust 里很常見&#xff1a; 一、結構體字段能否單獨加 mut 1. 結構體字段能否單獨加 mut&#xff1f; 不能。Rust 中&#xff0c;mut 是用來修飾變量綁定的&#xff0c;可變性是綁定的屬性&#xff0c;而不是結構體字段本身的屬性。 你不能寫&#xff1a; …

scGPT-spatial 復現

文章目錄 ? 總體流程總覽&#xff08;從 H5AD 到模型訓練&#xff09;&#x1f527; 步驟 1&#xff1a;讀取 H5AD 文件并做基礎預處理&#x1f9f1; 步驟 2&#xff1a;構造訓練樣本輸入&#xff08;token、value&#xff09;&#x1f4e6; 步驟 3&#xff1a;使用 DataColla…

運放電壓跟隨器為什么要加電阻

運放電壓跟隨器為什么要加電阻 我們常見運放的電壓跟隨器如下&#xff1a; 有時候會看見電路中加兩個電阻&#xff1a; 作用就是保護運放&#xff0c;起限流電阻的作用。 當輸入電壓高的時候&#xff0c;運放內部存在鉗位二極管&#xff0c;此電阻就能限流。 并不是所有運放…

MinerU 2.0部署

簡介 MinerU 2.0使用sglang加速&#xff0c;與之前差別較大&#xff0c;建議按照官方的Docker鏡像的方式啟動。 Docker鏡像 Dockerfile 這是官方的Dockerfile # Use the official sglang image FROM lmsysorg/sglang:v0.4.7-cu124# install mineru latest RUN python3 -m …

黑馬python(十七)

目錄&#xff1a; 1.數據可視化-地圖-基礎案例 2.全國疫情地圖 3.河南省疫情地圖繪制 4.基礎柱狀圖構建 5.基礎時間線柱狀圖繪制 6.動態GDP柱狀圖繪制 1.數據可視化-地圖-基礎案例 圖示有點對的不準&#xff0c;可以通過后面的參數 2.全國疫情地圖 3.河南省疫情地圖繪制…

Segment Anything in High Quality之SAM-HQ論文閱讀

摘要 最近的 Segment Anything Model(SAM)在擴展分割模型規模方面取得了重大突破,具備強大的零樣本能力和靈活的提示機制。盡管 SAM 在訓練時使用了 11 億個掩碼,其掩碼預測質量在許多情況下仍不理想,尤其是對于結構復雜的目標。我們提出了 HQ-SAM,使 SAM 能夠精確地分割…

深入理解_FreeRTOS的內部實現(2)

1.事件組 事件組結構體&#xff1a; 事件組 “不關中斷” 的核心邏輯 事件組操作時&#xff0c;優先選擇 “關調度器” 而非 “關中斷” &#xff0c;原因和實現如下&#xff1a; 關調度器&#xff08;而非關中斷&#xff09; FreeRTOS 提供 taskENTER_CRITICAL()&#xff08;…

【圖論題典】Swift 解 LeetCode 最小高度樹:中心剝離法詳解

文章目錄 摘要描述題解答案題解代碼分析思路來源&#xff1a;樹的“中心剝離法”構造鄰接表和度數組循環剝葉子終止條件 示例測試及結果時間復雜度空間復雜度總結 摘要 樹是一種重要的數據結構&#xff0c;在許多應用里&#xff0c;我們希望選一個根&#xff0c;讓這棵樹的高度…

Docker的介紹與安裝

? Docker 對初學者的簡單解釋和應用場景 1.什么是 Docker&#xff1f; 簡單來說&#xff0c;Docker 就像一個“裝箱子”的工具&#xff0c;這個箱子叫做“容器”。 你寫的程序和它運行需要的環境&#xff08;比如操作系統、軟件、工具&#xff09;都裝進一個箱子里。這個箱…

引導相機:工業自動化的智能之眼,賦能制造業高效升級

在工業自動化浪潮中&#xff0c;精準的視覺引導技術正成為生產效率躍升的關鍵。作為遷移科技——一家成立于2017年、專注于3D工業相機和3D視覺系統的領先供應商&#xff0c;我們深知"引導相機"的核心價值&#xff1a;它不僅是一個硬件設備&#xff0c;更是連接物理世…

智能相機如何重塑工業自動化?遷移科技3D視覺系統的場景革命

從硬件參數到產業價值&#xff0c;解碼高精度視覺系統的落地邏輯 一、工業視覺的“智慧之眼” 遷移科技深耕3D工業相機領域&#xff0c;以“穩定、易用、高回報”為核心理念&#xff0c;打造覆蓋硬件、算法、軟件的全棧式視覺系統。成立6年累計融資數億元的背后&#xff0c;是…