組合模式及優化

組合模式是一種結構型設計模式,其核心思想是將對象組合成樹形結構,以表示“部分-整體”的層次關系,使得用戶對單個對象和組合對象的使用具有一致性。

一、介紹

核心角色

組合模式包含以下3個關鍵角色:

  1. 抽象組件(Component)
    定義單個對象和組合對象的共同接口,聲明所有操作(如添加、刪除子節點、獲取子節點等)。
  2. 葉子節點(Leaf)
    表示樹形結構中的“單個對象”,沒有子節點。實現抽象組件的接口,但不支持“添加/刪除子節點”等操作(通常拋出異常)。
  3. 復合節點(Composite)
    表示樹形結構中的“組合對象”,可以包含子節點(葉子節點或其他復合節點)。實現抽象組件的接口,并重寫“添加/刪除子節點”等操作,通過管理子節點集合實現功能。
優點
  1. 一致性操作:用戶無需區分單個對象和組合對象,統一調用接口即可處理整個樹形結構。
  2. 擴展性強:新增葉子節點或復合節點時,無需修改現有代碼(符合開閉原則)。
  3. 簡化客戶端邏輯:客戶端無需編寫復雜的判斷邏輯(如“是否為組合對象”),直接遞歸處理即可。
  4. 清晰表示層次關系:通過樹形結構直觀體現“部分-整體”關系,便于理解和維護。
適用場景

當需要處理 具有“部分-整體”層次關系的對象結構,且希望用戶忽略單個對象和組合對象的差異時,適合使用組合模式。典型場景包括:

  • 樹形結構數據:如文件系統(文件夾與文件)、組織機構(部門與員工)、XML/JSON節點等。
  • UI組件:如按鈕(葉子)和面板(復合,包含按鈕/其他面板)。
  • 圖形繪制:如基本圖形(直線、圓)和組合圖形(由多個基本圖形組成)。

二、實現

以文件系統的為例,使用組合模式表示文件和文件夾的層次結構:

#include <iostream>
#include <vector>
#include <string>
#include <memory>// 抽象組件類:定義文件和文件夾的共同接口
class FileSystemComponent {
protected:std::string name_;public:explicit FileSystemComponent(std::string name) : name_(std::move(name)) {}virtual ~FileSystemComponent() = default;// 獲取名稱std::string getName() const {return name_;}// 純虛函數:顯示組件信息(聲明為純虛函數,使該類成為抽象類)virtual void display(int depth = 0) const = 0;// 虛函數:添加子組件(默認不實現,由容器類重寫)virtual void add(std::shared_ptr<FileSystemComponent> component) {throw std::runtime_error("不支持添加操作");}// 虛函數:移除子組件(默認不實現,由容器類重寫)virtual void remove(const std::string& name) {throw std::runtime_error("不支持移除操作");}// 虛函數:獲取子組件(默認不實現,由容器類重寫)virtual std::shared_ptr<FileSystemComponent> getChild(const std::string& name) {throw std::runtime_error("不支持獲取子組件操作");}
};// 葉子節點:文件
class File : public FileSystemComponent {
private:int size_;  // 文件大小(KB)public:File(std::string name, int size) : FileSystemComponent(std::move(name)), size_(size) {}// 顯示文件信息void display(int depth = 0) const override {std::string indent(depth, '-');std::cout << indent << "文件: " << name_ << " (" << size_ << "KB)" << std::endl;}
};// 容器節點:文件夾
class Folder : public FileSystemComponent {
private:std::vector<std::shared_ptr<FileSystemComponent>> children_;public:explicit Folder(std::string name) : FileSystemComponent(std::move(name)) {}// 添加子組件(文件或文件夾)void add(std::shared_ptr<FileSystemComponent> component) override {children_.push_back(std::move(component));}// 移除子組件void remove(const std::string& name) override {auto it = std::remove_if(children_.begin(), children_.end(),[&name](const std::shared_ptr<FileSystemComponent>& comp) {return comp->getName() == name;});if (it != children_.end()) {children_.erase(it, children_.end());}}// 獲取子組件std::shared_ptr<FileSystemComponent> getChild(const std::string& name) override {for (const auto& child : children_) {if (child->getName() == name) {return child;}}return nullptr;}// 顯示文件夾信息及所有子組件void display(int depth = 0) const override {std::string indent(depth, '-');std::cout << indent << "文件夾: " << name_ << " (包含 " << children_.size() << " 個項目)" << std::endl;// 遞歸顯示子組件,深度+1for (const auto& child : children_) {child->display(depth + 2);}}
};// 客戶端代碼
int main() {// 創建文件auto file1 = std::make_shared<File>("readme.txt", 10);auto file2 = std::make_shared<File>("image.png", 2048);auto file3 = std::make_shared<File>("data.csv", 512);auto file4 = std::make_shared<File>("notes.txt", 5);// 創建文件夾auto docsFolder = std::make_shared<Folder>("文檔");auto picsFolder = std::make_shared<Folder>("圖片");auto rootFolder = std::make_shared<Folder>("根目錄");// 構建文件系統結構docsFolder->add(file1);docsFolder->add(file4);picsFolder->add(file2);rootFolder->add(docsFolder);rootFolder->add(picsFolder);rootFolder->add(file3);// 顯示整個文件系統(通過根節點統一操作)std::cout << "文件系統結構:" << std::endl;rootFolder->display();return 0;
}   
輸出結果
文件系統結構:
文件夾: 根目錄 (包含 3 個項目)
--文件夾: 文檔 (包含 2 個項目)
----文件: readme.txt (10KB)
----文件: notes.txt (5KB)
--文件夾: 圖片 (包含 1 個項目)
----文件: image.png (2048KB)
--文件: data.csv (512KB)
應用場景
  1. 文件系統
    • 文件夾和文件組成的樹形結構,支持統一的操作接口
  2. UI框架
    • 容器控件(如面板、窗口)包含其他控件(如按鈕、文本框),形成樹形結構
  3. 組織結構
    • 公司包含部門,部門包含小組,小組包含員工,形成層次結構
  4. 圖形系統
    • 復雜圖形由簡單圖形組合而成,如組合圖形(CompositeShape)包含多個基本圖形(Circle、Rectangle)
  5. 菜單系統
    • 菜單欄包含菜單,菜單包含菜單項或子菜單,形成樹形結構

三、優化

優化點
  1. 泛型設計
    • 使用模板實現通用組件,支持任意數據類型(示例中用int表示文件大小)
    • 同一套組合模式可適用于不同業務場景(文件系統、UI組件、組織機構等)
  2. 類型安全與錯誤處理
    • 增加組件類型判斷(isComposite()
    • 防止添加空組件、自身作為子組件等非法操作
    • 使用異常機制處理錯誤,提供更友好的錯誤信息
  3. 迭代器支持
    • 實現ComponentIterator接口,支持統一遍歷組合組件
    • 客戶端可通過迭代器訪問子組件,無需了解內部存儲結構
  4. 功能擴展接口
    • 增加getSize()方法,支持遞歸計算總大小
    • 組合組件添加countLeaves()方法,統計葉子節點數量
    • 保留擴展空間,可根據需求添加更多聚合操作
  5. 現代C++特性
    • 使用std::shared_ptr管理組件生命周期,避免內存泄漏
    • 利用STL算法(accumulateremove_if)簡化代碼
    • 使用override關鍵字明確重寫關系,增強代碼可讀性
#include <iostream>
#include <vector>
#include <string>
#include <memory>
#include <algorithm>
#include <iterator>
#include <numeric>
#include <typeinfo>
#include <stdexcept>// 前向聲明
template <typename T>
class Component;// 迭代器接口 - 支持遍歷組件
template <typename T>
class ComponentIterator {
public:using iterator_category = std::forward_iterator_tag;using value_type = std::shared_ptr<Component<T>>;using difference_type = std::ptrdiff_t;using pointer = value_type*;using reference = value_type&;virtual ~ComponentIterator() = default;virtual bool hasNext() const = 0;virtual value_type next() = 0;virtual void reset() = 0;
};// 抽象組件基類 - 泛型設計支持不同類型組件
template <typename T>
class Component {
protected:std::string name_;T data_;  // 組件攜帶的數據public:explicit Component(std::string name, T data = T{}) : name_(std::move(name)), data_(std::move(data)) {}virtual ~Component() = default;// 基礎接口std::string getName() const { return name_; }T getData() const { return data_; }void setData(T data) { data_ = std::move(data); }// 純虛接口 - 必須實現virtual void display(int depth = 0) const = 0;virtual bool isComposite() const = 0;  // 區分葉子和組合// 組合操作 - 默認拋出異常,組合組件需重寫virtual void add(std::shared_ptr<Component<T>>) {throw std::runtime_error("不支持添加操作: " + name_);}virtual void remove(const std::string& name) {throw std::runtime_error("不支持移除操作: " + name_);}virtual std::shared_ptr<Component<T>> getChild(const std::string& name) {throw std::runtime_error("不支持獲取子組件: " + name_);}// 迭代器支持virtual std::unique_ptr<ComponentIterator<T>> createIterator() {throw std::runtime_error("不支持迭代器: " + name_);}// 功能擴展接口 - 計算組件大小(示例)virtual size_t getSize() const = 0;
};// 葉子組件 - 不能包含子組件
template <typename T>
class Leaf : public Component<T> {
public:Leaf(std::string name, T data = T{}) : Component<T>(std::move(name), std::move(data)) {}void display(int depth = 0) const override {std::string indent(depth, '-');std::cout << indent << "葉子: " << this->name_ << " (數據: " << this->data_ << ")" << std::endl;}bool isComposite() const override { return false; }// 葉子組件大小即為自身大小size_t getSize() const override {return sizeof(*this);  // 實際應用中可返回真實數據大小}
};// 組合組件迭代器實現
template <typename T>
class CompositeIterator : public ComponentIterator<T> {
private:std::vector<std::shared_ptr<Component<T>>> children_;size_t currentIndex_ = 0;public:explicit CompositeIterator(std::vector<std::shared_ptr<Component<T>>> children): children_(std::move(children)) {}bool hasNext() const override {return currentIndex_ < children_.size();}typename ComponentIterator<T>::value_type next() override {if (!hasNext()) {throw std::out_of_range("迭代器已到達末尾");}return children_[currentIndex_++];}void reset() override {currentIndex_ = 0;}
};// 組合組件 - 可以包含子組件
template <typename T>
class Composite : public Component<T> {
private:std::vector<std::shared_ptr<Component<T>>> children_;// 類型檢查輔助函數template <typename U>bool isType(const std::shared_ptr<Component<T>>& component) const {return typeid(*component) == typeid(U);}public:explicit Composite(std::string name, T data = T{}) : Component<T>(std::move(name), std::move(data)) {}// 添加子組件(帶類型檢查)void add(std::shared_ptr<Component<T>> component) override {if (!component) {throw std::invalid_argument("不能添加空組件");}if (component.get() == this) {throw std::invalid_argument("不能添加自身作為子組件");}children_.push_back(std::move(component));}// 移除子組件void remove(const std::string& name) override {auto it = std::remove_if(children_.begin(), children_.end(),[&name](const std::shared_ptr<Component<T>>& comp) {return comp->getName() == name;});if (it != children_.end()) {children_.erase(it, children_.end());} else {throw std::out_of_range("未找到子組件: " + name);}}// 獲取子組件std::shared_ptr<Component<T>> getChild(const std::string& name) override {for (const auto& child : children_) {if (child->getName() == name) {return child;}}return nullptr;}// 顯示組件及子組件void display(int depth = 0) const override {std::string indent(depth, '-');std::cout << indent << "組合: " << this->name_ << " (包含 " << children_.size() << " 個子組件)" << std::endl;// 遞歸顯示子組件for (const auto& child : children_) {child->display(depth + 2);}}bool isComposite() const override { return true; }// 創建迭代器std::unique_ptr<ComponentIterator<T>> createIterator() override {return std::make_unique<CompositeIterator<T>>(children_);}// 計算總大小(遞歸計算所有子組件)size_t getSize() const override {return std::accumulate(children_.begin(), children_.end(), sizeof(*this),  // 自身大小[](size_t total, const std::shared_ptr<Component<T>>& child) {return total + child->getSize();});}// 擴展功能:統計葉子節點數量size_t countLeaves() const {size_t count = 0;for (const auto& child : children_) {if (child->isComposite()) {// 向下轉型調用組合組件的方法auto composite = std::dynamic_pointer_cast<Composite<T>>(child);if (composite) {count += composite->countLeaves();}} else {count++;}}return count;}
};// 客戶端代碼 - 文件系統示例
int main() {try {// 創建文件(葉子組件,數據為文件大小KB)auto file1 = std::make_shared<Leaf<int>>("readme.txt", 10);auto file2 = std::make_shared<Leaf<int>>("image.png", 2048);auto file3 = std::make_shared<Leaf<int>>("data.csv", 512);auto file4 = std::make_shared<Leaf<int>>("notes.txt", 5);// 創建文件夾(組合組件)auto docsFolder = std::make_shared<Composite<int>>("文檔");auto picsFolder = std::make_shared<Composite<int>>("圖片");auto rootFolder = std::make_shared<Composite<int>>("根目錄");// 構建層次結構docsFolder->add(file1);docsFolder->add(file4);picsFolder->add(file2);rootFolder->add(docsFolder);rootFolder->add(picsFolder);rootFolder->add(file3);// 顯示整個結構std::cout << "文件系統結構:" << std::endl;rootFolder->display();// 使用迭代器遍歷根目錄子組件std::cout << "\n根目錄子組件列表:" << std::endl;auto iterator = rootFolder->createIterator();while (iterator->hasNext()) {auto comp = iterator->next();std::cout << "- " << comp->getName() << (comp->isComposite() ? " (文件夾)" : " (文件)") << std::endl;}// 功能擴展演示std::cout << "\n統計信息:" << std::endl;std::cout << "總大小: " << rootFolder->getSize() << " 字節" << std::endl;std::cout << "文件總數: " << rootFolder->countLeaves() << " 個" << std::endl;// 測試錯誤處理try {file1->add(file2);  // 葉子組件不能添加子組件} catch (const std::exception& e) {std::cout << "\n錯誤處理測試: " << e.what() << std::endl;}} catch (const std::exception& e) {std::cerr << "發生錯誤: " << e.what() << std::endl;return 1;}return 0;
}   
輸出結果
文件系統結構:
組合: 根目錄 (包含 3 個子組件)
--組合: 文檔 (包含 2 個子組件)
----葉子: readme.txt (數據: 10)
----葉子: notes.txt (數據: 5)
--組合: 圖片 (包含 1 個子組件)
----葉子: image.png (數據: 2048)
--葉子: data.csv (數據: 512)根目錄子組件列表:
- 文檔 (文件夾)
- 圖片 (文件夾)
- data.csv (文件)統計信息:
總大小: 40 字節
文件總數: 4 個錯誤處理測試: 不支持添加操作: readme.txt
適用場景擴展

優化后的組合模式更適合:

  • 復雜樹形結構的管理(如多級菜單、嵌套控件)
  • 需要統一遍歷接口的場景(如遞歸計算、搜索)
  • 頻繁擴展功能的系統(通過擴展接口添加新操作)
  • 對類型安全和內存管理有嚴格要求的生產環境

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

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

相關文章

【wmi異常】關于taskkill命令提示“錯誤:找不到” 以及無法正常獲取設備機器碼的處理辦法

記錄一下我的解決方案。 我先查閱了這篇博客&#xff1a;https://blog.csdn.net/qq_45698181/article/details/138957277 發現他寫的批處理不知怎么執行不了&#xff0c;后來問了ai又可以執行了&#xff0c;估計是csdn防盜版格式問題 這里寫一下我跟ai的對話&#xff0c;大家可…

制造裝配、倉儲搬運、快遞裝卸皆適配!MinkTec 彎曲形變傳感器助力,讓人體工學改變勞動生活

【導語】Minktec 最新實驗顯示&#xff1a;將Minktec 柔性彎曲形變傳感器FlexTail 貼于受試者背部&#xff0c;記錄 1 分鐘內從洗碗機取餐具的動作&#xff0c;結合配套的flexlib -專用Python庫分析&#xff0c;不僅量化出 “越低越傷腰” 的結論&#xff0c;更為制造裝配、物流…

Nginx蜘蛛請求智能分流:精準識別爬蟲并轉發SEO渲染服務

> 一招解決搜索引擎爬蟲無法解析現代前端框架的痛點,提升網站收錄率與SEO排名! **痛點場景**:你的網站采用Vue/React等前端框架構建,頁面內容依賴JavaScript動態渲染。搜索引擎爬蟲訪問時,只能抓取到空HTML骨架,無法獲取真實內容,導致網站收錄率低、SEO效果差。 --…

鏈表。。。

目錄 5.1 鏈表的結點 5.2 插入 5.3 鏈表長度 5.4 查找 5.5 指定位置刪除 5.6 代碼 5.1 鏈表的結點 一個結點包括&#xff1a;值和指向下一個結點的指針。 package com.qcby.鏈表;public class Node {int value;Node next;public Node(int val){valueval;}Overridepublic…

私人AI搜索新突破:3步本地部署Dify+Ollama+QwQ,搜索能力MAX

1.安裝Docker容器 本地部署Dify要先安裝Docker桌面版&#xff0c;跟Ollama一樣簡單&#xff0c;也是去官網下載對應版本文件&#xff0c;直接安裝就OK。 2&#xff1a;安裝Dify 安裝 Dify 簡單的方式就是git clone&#xff0c;復制其github地址github.com/langgenius/dify&am…

(2-10-1)MyBatis的基礎與基本使用

目錄 0.前置小節 1. MyBatis 框架介紹 1.1 軟件開發中的框架 1.2 使用框架的好處 1.3 SSM 開發框架 1.4 什么是 MyBatis 1.5 MyBatis 的開發流程 2. MyBatis 的開發流程 2.0 MyBatis的工作流程 2.1 引入 MyBatis 依賴 00.base(目錄、pom、單元測試、Junit4) 01.Cal…

StarRocks集群部署

Starrocks 是一款基于 MPP 架構的高性能實時分析型數據庫&#xff0c;專為 OLAP&#xff08;聯機分析處理&#xff09;場景 設計&#xff0c;尤其擅長處理海量數據的實時分析、復雜查詢和多維統計。 硬件 CPU&#xff1a;StarRocks依靠AVX2指令集充分發揮其矢量化能力。因此&am…

【CPP】自己實現一個CPP小工具demo,可以擴展其他選項

自己寫CPP腳本小工具1. 思路描述2. 代碼實現2.1 代碼文件CppTool.cpp2.2 CMakeLists.txt3. 工具示例3.1 幫助信息3.2 工具用法3.3 實際使用1. 思路描述 實現一個簡單的命令行工具。內容包括&#xff1a; 命令幫助信息參數檢查&#xff0c;參數解析等功能。執行其他命令。將指…

如何使用嵌入模型創建本地知識庫Demo

為data目錄下的txt文檔用阿里百煉的文本嵌入模型創建一個本地知識庫import os from llama_index.core import ,Settings, SimpleDirectoryReader, VectorStoreIndex from llama_index.core.node_parser import SentenceSplitter from llama_index.llms.dashscope import DashSc…

SpringBoot 整合 Langchain4j:系統提示詞與用戶提示詞實戰詳解

> 掌握提示詞工程的核心技巧,讓你的AI應用效果提升300%! **真實痛點**:為什么同樣的模型,別人的應用精準專業,而你的卻答非所問?關鍵在于提示詞工程!本文將揭秘如何通過系統提示詞與用戶提示詞的巧妙配合,打造專業級AI應用。 --- ### 一、Langchain4j 核心概念…

Sklearn 機器學習 郵件文本分類 加載郵件數據

??親愛的技術愛好者們,熱烈歡迎來到 Kant2048 的博客!我是 Thomas Kant,很開心能在CSDN上與你們相遇~?? 本博客的精華專欄: 【自動化測試】 【測試經驗】 【人工智能】 【Python】 Sklearn 機器學習 郵件文本分類 - 加載郵件數據 在自然語言處理(NLP)中,郵件文本分…

騰訊云開發小程序工具箱使用心得

一、核心優勢與使用體驗 作為首批使用騰訊云開發&#xff08;CloudBase&#xff09;工具箱的開發者&#xff0c;我深刻感受到其通過CloudBase AI與MCP服務重構開發范式的創新價值。結合微信小程序開發場景&#xff0c;該平臺在以下維度表現突出&#xff1a; 1. AI驅動的全棧開發…

機械加工元件——工業精密制造的璀璨明珠

在工業制造的宏大畫卷中&#xff0c;機械加工元件猶如璀璨的明珠&#xff0c;以其卓越的性能和精湛的工藝&#xff0c;為各行各業的發展注入了源源不斷的動力。它們雖形態各異&#xff0c;功能不同&#xff0c;卻在無數產品中攜手合作&#xff0c;展現出科技與柔性的完美融合。…

【八股】Redis-中小廠精要八股

Redis 基礎 redis為什么這么快 (高) [!NOTE] 最首要的是Redis是純內存操作, 比磁盤要快3個數量級同時在與內存操作中采用了非阻塞I/O多路復用機制來提高并發量并且基于Redis的IO密集型&#xff0c;采用單線程操作, 免去了線程切換開銷Redis 內置了多種優化過后的數據結構實現…

C++字符串(string)操作解析:從基礎到進階

1. 字符串基礎&#xff1a;大小與容量cppvoid test1() {string s1("Hello World");cout << "size : " << s1.size() << endl; // 輸出字符串長度cout << "capacity " << s1.capacity() << endl; // 輸出字…

蘑兔音樂:音樂創作的魔法棒

在這個充滿創意與可能的時代&#xff0c;人人都有一顆渴望表達音樂之心。但傳統音樂創作&#xff0c;復雜的樂理、昂貴的設備&#xff0c;總讓人望而卻步。別擔心&#xff01;蘑兔 AI 音樂強勢來襲&#xff0c;它就是那個能讓音樂小白也能搞創作的神奇工具&#xff01;?靈感模…

從傳統到智能:RFID 技術如何重構壓縮機生產線

從傳統到智能&#xff1a;RFID 技術如何重構壓縮機生產線在工業 4.0 與中國制造 2025 戰略的深入推進下&#xff0c;作為空調核心部件的壓縮機制造業正加速從傳統生產模式向智能化轉型。壓縮機生產以高精度、大批量為顯著特點&#xff0c;長期面臨生產數據斷層、柔性化不足、質…

HTML5二十四節氣網站源碼

一. 二十四節氣文化主題網站概述 本網站以中國傳統文化瑰寶“二十四節氣”為核心&#xff0c;通過現代Web技術打造沉浸式文化體驗平臺&#xff0c;融合視覺美學與交互創新&#xff0c;全方位展現節氣的自然規律與人文內涵。網站采用響應式布局設計&#xff0c;適配多終端設備&…

微服務架構實戰指南:從單體應用到云原生的蛻變之路

&#x1f31f; Hello&#xff0c;我是蔣星熠Jaxonic&#xff01; &#x1f308; 在浩瀚無垠的技術宇宙中&#xff0c;我是一名執著的星際旅人&#xff0c;用代碼繪制探索的軌跡。 &#x1f680; 每一個算法都是我點燃的推進器&#xff0c;每一行代碼都是我航行的星圖。 &#x…

超越Transformer:大模型架構創新的深度探索

引言&#xff1a; 以GPT、Claude、Gemini等為代表的大語言模型&#xff08;LLMs&#xff09;已成為人工智能領域的核心驅動力。它們基于Transformer架構構建&#xff0c;在理解和生成人類語言方面展現出驚人的能力。然而&#xff0c;隨著模型規模指數級增長和對更長上下文、更高…