復合設計模式

復合設計模式

復合設計模式是一種結構模式,可讓您統一處理單個對象和對象的組合。

它允許您構建樹狀結構(例如,文件系統、UI 層次結構、組織結構),客戶端可以使用同一界面處理單個元素和元素組。

它在以下情況下特別有用:
您需要表示部分-整體層次結構。

您希望 以一致的方式對葉節點和復合節點執行作。
您希望避免編寫特殊情況邏輯來區分“單個”和“分組”對象。

在設計此類系統時,開發人員通常從 塊或類型檢查開始,以不同于集合的方式處理單個項目。例如, 在決定執行什么作之前,方法可能必須檢查元素是按鈕、面板還是容器。if-elserender()

但是,隨著結構的復雜性增加,這種方法變得難以擴展,違反了開放/關閉原則,并在客戶端代碼和結構的內部組合之間引入了緊密耦合。

復合圖案通過為所有元素定義一個通用接口來解決這個問題,無論它們是葉子還是復合元素。然后可以以相同的方式處理每個組件——允許客戶端像簡單對象一樣對復雜的結構進行作。

讓我們通過一個真實世界的示例,看看如何應用復合模式來建模一個既干凈又可擴展的靈活分層系統。

問題:對文件資源管理器進行建模
想象一下,您正在構建一個文件資源管理器應用程序(例如 macOS 上的 Finder 或 Windows 上的文件資源管理器)。系統需要表示:

文件 – 具有名稱和大小的簡單項目。
文件夾 – 可以保存文件 和其他文件夾(甚至嵌套文件夾)的容器。

您的目標是支持以下作:
getSize()– 返回文件或文件夾的總大小(這是所有內容的總和)。
printStructure()– 打印項目的名稱,包括縮進以顯示層次結構。
delete()– 刪除文件或文件夾及其中的所有內容。

天真的方法
一個簡單的解決方案可能涉及兩個單獨的類: 和 。這是一個簡化版本:FileFolder

文件

???class?File?{private?String?name;private?int?size;public?int?getSize()?{return?size;}public?void?printStructure(String?indent)?{System.out.println(indent?+?name);}public?void?delete()?{System.out.println("Deleting?file:?"?+?name);}
}

文件夾

???import?java.util.ArrayList;
import?java.util.List;class?Folder?{private?String?name;private?List<Object>?contents?=?new?ArrayList<>();public?int?getSize()?{int?total?=?0;for?(Object?item?:?contents)?{if?(item?instanceof?File)?{total?+=?((File)?item).getSize();}?else?if?(item?instanceof?Folder)?{total?+=?((Folder)?item).getSize();}}return?total;}public?void?printStructure(String?indent)?{System.out.println(indent?+?name?+?"/");for?(Object?item?:?contents)?{if?(item?instanceof?File)?{((File)?item).printStructure(indent?+?"??");}?else?if?(item?instanceof?Folder)?{((Folder)?item).printStructure(indent?+?"??");}}}public?void?delete()?{for?(Object?item?:?contents)?{if?(item?instanceof?File)?{((File)?item).delete();}?else?if?(item?instanceof?Folder)?{((Folder)?item).delete();}}System.out.println("Deleting?folder:?"?+?name);}

這種方法有什么問題?
隨著結構變得越來越復雜,該解決方案引入了幾個關鍵問題:

  1. 1. 重復類型檢查
    像 、 和 這樣的作 需要重復 檢查和向下轉換——導致邏輯重復和脆弱。getSize()printStructure()delete()instanceof
  2. 2. 沒有共享抽象
    和 沒有通用接口,這意味著您不能統一對待它們。你不能編寫這樣的代碼:FileFolder
???List<FileSystemItem>?items?=?List.of(file,?folder);
for?(FileSystemItem?item?:?items)?{item.delete();
}
  1. 3. 違反開放/關閉原則
    要添加新的項目類型(例如 , ),您必須修改發生類型檢查的每個位置的現有邏輯,這會增加錯誤和回歸的風險。ShortcutCompressedFolder
  2. 4. 缺乏遞歸優雅
    刪除深度嵌套的文件夾或跨多個級別計算大小會變成嵌套條件和遞歸檢查的混亂。

我們真正需要什么
我們需要一個解決方案:

為所有組件引入通用接口(例如,)。FileSystemItem
允許 通過多態性統一處理文件和文件夾。
使文件夾能夠包含同一接口的列表,支持任意嵌套。
支持遞歸作,如 delete 和 getSize,無需類型檢查。

使系統易于擴展 — 無需修改現有邏輯即可添加新的項目類型。
這正是復合設計模式所針對的問題。

復合模式
復合設計模式是一種結構設計模式,可讓您以統一的方式處理單個對象和對象組。

在復合結構中,層次結構中的每個節點共享相同的接口,無論是葉子(例如 a )還是復合節點(例如 a )。這允許客戶端在 兩者之間遞歸一致地執行 、 或 等作。FileFoldergetSize()delete()render()

類圖

組件接口(例如: 聲明所有具體組件的通用接口)FileSystemItem
葉子(例如): 表示最終對象(無子對象)File
復合(例如): 表示可以容納子項(包括其他復合)的對象Folder
客戶端(例如): 使用共享界面處理組件FileExplorerApp

實現復合
我們將首先為文件系統中的所有項目定義一個通用接口,允許統一處理文件和文件夾。

  1. 1. 定義組件接口
???public?interface?FileSystemItem?{int?getSize();void?printStructure(String?indent);void?delete();
}

此接口可確保所有文件系統項(無論是文件還是文件夾)向客戶端公開相同的行為。

  1. 2. 創建 Leaf 類 –File
??public?class?File?implements?FileSystemItem?{private?final?String?name;private?final?int?size;public?File(String?name,?int?size)?{this.name?=?name;this.size?=?size;}@Overridepublic?int?getSize()?{return?size;}@Overridepublic?void?printStructure(String?indent)?{System.out.println(indent?+?"-?"?+?name?+?"?("?+?size?+?"?KB)");}@Overridepublic?void?delete()?{System.out.println("Deleting?file:?"?+?name);}
}

每個 都是葉節點。它不包含任何子項。File

  1. 3. 創建復合類 –Folder
??import?java.util.ArrayList;
import?java.util.List;public?class?Folder?implements?FileSystemItem?{private?final?String?name;private?final?List<FileSystemItem>?children?=?new?ArrayList<>();public?Folder(String?name)?{this.name?=?name;}public?void?addItem(FileSystemItem?item)?{children.add(item);}@Overridepublic?int?getSize()?{int?total?=?0;for?(FileSystemItem?item?:?children)?{total?+=?item.getSize();}return?total;}@Overridepublic?void?printStructure(String?indent)?{System.out.println(indent?+?"+?"?+?name?+?"/");for?(FileSystemItem?item?:?children)?{item.printStructure(indent?+?"??");}}@Overridepublic?void?delete()?{for?(FileSystemItem?item?:?children)?{item.delete();}System.out.println("Deleting?folder:?"?+?name);}
}

該 類是復合類。它可以同時包含 和 實例,使其具有遞歸性和可擴展性。FolderFileFolder

  1. 4. 客戶端代碼
??public?class?FileExplorerApp?{public?static?void?main(String[]?args)?{FileSystemItem?file1?=?new?File("readme.txt",?5);FileSystemItem?file2?=?new?File("photo.jpg",?1500);FileSystemItem?file3?=?new?File("data.csv",?300);Folder?documents?=?new?Folder("Documents");documents.addItem(file1);documents.addItem(file3);Folder?pictures?=?new?Folder("Pictures");pictures.addItem(file2);Folder?home?=?new?Folder("Home");home.addItem(documents);home.addItem(pictures);System.out.println("----?File?Structure?----");home.printStructure("");System.out.println("\nTotal?Size:?"?+?home.getSize()?+?"?KB");System.out.println("\n----?Deleting?All?----");home.delete();}
}

輸出

----?File?Structure?----
+?Home/
+?Documents/-?readme.txt?(5?KB)-?data.csv?(300?KB)
+?Pictures/-?photo.jpg?(1500?KB)Total?Size:?1805?KB----?Deleting?All?----
Deleting?file:?readme.txt
Deleting?file:?data.csv
Deleting?folder:?Documents
Deleting?file:?photo.jpg
Deleting?folder:?Pictures
Deleting?folder:?Home

使用復合模式,我們按照文件系統的自然工作方式對文件系統進行了建模,將其建模為項目樹,其中一些是葉子,另一些是容器。每個作 (, , ) 現在都是模塊化的、遞歸的和可擴展的。getSize()printStructure()delete()

我們通過復合材料取得了什么成就?
統一處理:文件和文件夾共享一個通用界面,允許多態性

干凈遞歸:不,沒有鑄造——只是委托instanceof

可擴展性:輕松支持深度嵌套結構

可維護性:添加新文件類型(例如,Shortcut、CompressedFolder)很容易

可擴展性:可以通過接口擴展或訪問者模式添加新作

其他閱讀材料:

https://pan.baidu.com/s/1c1oQItiA7nZxz8Rnl3STpw?pwd=yftc
https://pan.quark.cn/s/dec9e4868381

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

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

相關文章

使用 Prometheus 監控服務器節點:Node Exporter 詳解與配置

前言 在上一篇文章中&#xff0c;我們介紹了如何在 CentOS 上部署 Prometheus 并使用 systemd 進行管理。本文將繼續深入&#xff0c;講解如何使用 Prometheus 監控服務器節點&#xff0c;重點介紹 Node Exporter 的作用、安裝和配置方法。 Node Exporter 是 Prometheus 生態…

C# 編寫一個XmlToDota的轉換工具

以下代碼可以將Labelme標注的旋轉框Xml格式文件轉換為Yolo標注格式的txt文件&#xff0c;以便用Yolo OBB訓練自己的數據集&#xff1a;using System; using System.Collections.Generic; using System.IO; using System.Xml; using System.Linq; using System.Globalization;na…

[Android] 人體細胞模擬器1.5

[Android] 人體細胞模擬器1.5 鏈接&#xff1a;https://pan.xunlei.com/s/VOYVUieTpjNVJq-bMys4EEDGA1?pwdm7m6# 省流:這個軟件的開發者有點逆天&#xff0c;一個模擬人體器官的軟件&#xff0c;細致到有血液報告&#xff0c;還縫合了生理學和病理學&#xff0c;甚至還能做切…

【Linux基礎知識系列】第一百一十篇 - 使用Nmap進行網絡安全掃描

在網絡安全管理中&#xff0c;了解網絡中的設備、開放的端口以及運行的服務是至關重要的。Nmap&#xff08;Network Mapper&#xff09;是一個功能強大的開源工具&#xff0c;用于網絡發現和安全審計。它可以掃描網絡中的設備&#xff0c;識別開放的端口和運行的服務&#xff0…

【Linux倉庫】進程的“奪舍”與“飛升”:exec 驅動的應用現代化部署流水線

&#x1f31f; 各位看官好&#xff0c;我是egoist2023&#xff01; &#x1f30d; Linux Linux is not Unix &#xff01; &#x1f680; 今天來學習exec系列的進程程序替換,從"fork"的"克隆"到"exec"的"重生"。 &#x1f44d; 如果覺…

Reachability Query

題目分析 該代碼實現了一個動態集合管理系統&#xff0c;支持三種操作&#xff1a;合并集合、切換元素狀態、查詢集合中是否- 存在活躍元素。核心數據結構為并查集&#xff0c;結合狀態標記數組和計數器。關鍵數據結構與函數 初始化 fa[N]&#xff1a;并查集父節點數組&#xf…

SSL移動接入方案和移動資源發布

一、SSL VPN概述SSL VPN是一種基于SSL/TLS協議的遠程安全接入技術&#xff0c;因其廣泛兼容Web瀏覽器&#xff0c;支持“無客戶端”部署&#xff0c;具備易于使用和維護的特點。它通過插件系統支持非Web類TCP/UDP應用&#xff0c;并且支持對用戶的訪問可以做出限制&#xff0c;…

C++STL---count() 統計容器中特定元素出現次數

在 C 標準庫中&#xff0c;count 是一個用于統計容器中特定元素出現次數的函數&#xff0c;定義在 <algorithm> 頭文件中。它可以快速計算某個值在容器&#xff08;如數組、vector、list 等&#xff09;中出現的次數&#xff0c;避免手動編寫循環計數的麻煩。 一、函數原…

Tesla自動駕駛域控制器(AutoPilot HW)的系統化梳理

目前網絡上對Tesla自動駕駛硬件&#xff08;AP1-AP4、HW1.0-HW4.0&#xff09;迭代的相關介紹比較混亂&#xff0c;本文這里進行系統化梳理并澄清&#xff0c;并對一些錯誤進行更正。1、AutoPilot HW迭代圖圖1 AutoPilot HWMCU迭代圖圖2 AutoPilot HW 散熱設計迭代圖&#xff0…

C 語言:第 20 天筆記:typedef(類型重命名規則、應用場景與實戰案例)

C語言&#xff1a;第20天筆記 內容提要 構造類型枚舉類型typedef綜合案例:斗地主預處理 構造類型&#xff1a;枚舉類型 使用建議 如果定義不相干的常量&#xff0c;使用宏定義&#xff08;符號常量&#xff09;&#xff1b;如果需要定義一組相關聯的常量&#xff08;如月份011、…

在 vue3 和 vue2 中,v-for 和 v-if 可以一起用嗎,區別是什么

在 Vue 2 和 Vue 3 中&#xff0c;v-for 和 v-if 可以一起使用&#xff0c;但兩者在處理順序和推薦用法上存在明顯區別&#xff0c;主要體現在優先級和最佳實踐上&#xff1a; 1. Vue 2 中的 v-for 與 v-if優先級&#xff1a;v-for 的優先級高于 v-if。 這意味著 Vue 會先循環渲…

Linux-進程相關函數

文章目錄Linux-進程相關函數父子進程關系父子進程地址空間getpid函數 獲取本進程號getppid函數 獲取當前進程的進程的父進程號getpgid函數 獲取進程組號示例fork函數 創建進程區分父子進程exit函數 進程退出wait函數 等待子進程退出waitpid函數Linux-進程相關函數 每個進程都由…

數據挖掘 6.1 其他降維方法(不是很重要)

6.1 Other dimensionality reduction methods 6.1 其他降維方法 其他降維方法前言問題答案流形3 降維大綱3.1 線性方法3.2 非線性方法3.2.1 流形學習方法&#xff08;Manifold Learning&#xff09;3.2.2 概率方法&#xff08;Probabilistic Approaches&#xff09;3.2.3 拓撲數…

Unity中的特殊文件夾

一.工程路徑獲取print(Application.dataPath);只用于游戲開發編輯器模式下&#xff0c;游戲發布后此路徑就不存在了二.Resources 資源文件夾//路徑獲取: //一般不獲取 //只能使用Resources相關API進行加載 //如果硬要獲取 可以用工程路徑拼接print(Application.dataPath "…

Seaborn數據可視化實戰:Seaborn高級使用與性能優化教程

Seaborn最佳實踐與技巧 學習目標 本課程將深入探討Seaborn庫的高級使用技巧&#xff0c;包括性能優化、常見問題解決方法等&#xff0c;旨在幫助學員掌握如何高效地使用Seaborn進行數據可視化&#xff0c;提升圖表的美觀度和信息傳達效率。 相關知識點 Seaborn最佳實踐與技巧 學…

分布式系統與單機系統的優劣勢對比

近期有遇到一個本地部署的需求&#xff0c;他們希望用主備方案&#xff0c;這就涉及到了備用系統怎么收費的問題。我們是單機系統&#xff0c;其他友商是分布式系統&#xff0c;那20坐席的手撥需求到底是選單機系統好&#xff0c;還是選分布式系統好呢&#xff1f;了解了兩者的…

深度學習:從手寫數字識別案例認識pytorch框架

目錄 一、PyTorch 核心優勢與框架定位 二、實戰基礎&#xff1a;核心庫與數據準備 1. 關鍵庫導入與功能說明 2. MNIST 數據集加載與可視化 &#xff08;1&#xff09;數據集下載與封裝 &#xff08;2&#xff09;數據集可視化&#xff08;可選&#xff09; 3. DataLoade…

二分|組合|旋轉數組

lc1976dijk min_pathpq. min_wlcr187同lc1823.約瑟夫環class Solution { public:int iceBreakingGame(int num, int target) {int x0;for(int i2;i<num;i){x(xtarget)%i;} return x;} };lc2972計算數組中可移除的子數組數量先找最長遞增前綴&#xff0c;再結合遞增后綴…

【C語言16天強化訓練】從基礎入門到進階:Day 10

&#x1f525;個人主頁&#xff1a;艾莉絲努力練劍 ?專欄傳送門&#xff1a;《C語言》、《數據結構與算法》、C語言刷題12天IO強訓、LeetCode代碼強化刷題、洛谷刷題、C/C基礎知識知識強化補充、C/C干貨分享&學習過程記錄 &#x1f349;學習方向&#xff1a;C/C方向學習者…

云計算與云原生技術探索

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