設計模式學習[17]---組合模式

文章目錄

  • 前言
    • 1.引例
    • 2.一致性抽象處理
    • 3.透明組合模式與安全組合模式
  • 總結

前言

在畫類圖的時候,類與類之間有組合關系,聚合關系,我本來以為這個組合模式應該是整體與部分的關系,其實設計模式中的組合模式和類圖中的組合不是同一個東西。設計模式中的組合模式更像是一個行為統一且存在結構性的關系,例如樹形結構。

1.引例

先看下面一個例子,在資源管理器中的文件目錄中有這樣的一個文件結構,root下面有common.mk,config.mk,makefile三個文件,以及app,signal,_include三個子文件夾,且子文件夾下還有對應的文件。
左側是結構圖,右側是樹形圖。
在這里插入圖片描述在這里插入圖片描述

現在我們用一段代碼來實現

#include <iostream>
#include <string>
#include <list>class File
{
public:File(std::string name) :m_sname(name) {}//顯示文件名void ShowName(std::string lvlstr){std::cout<<lvlstr << "-" << m_sname << std::endl;}private:std::string m_sname;	//文件名
};class Dir
{
public:Dir(std::string name) :m_sname(name) {};void AddFile(File* pfile){m_childFile.push_back(pfile);}void AddDir(Dir* pDir){m_childDir.push_back(pDir);}void ShowName(std::string lvlstr){//輸出本目錄名std::cout << lvlstr << "+" << m_sname << std::endl;//輸出所包含的文件名lvlstr += "  ";for (auto iter=m_childFile.begin();iter!=m_childFile.end();++iter){(*iter)->ShowName(lvlstr);}//輸出所包含的目錄名for (auto iter = m_childDir.begin(); iter != m_childDir.end(); ++iter){(*iter)->ShowName(lvlstr);}}
private:std::string m_sname;std::list<File*>m_childFile;std::list<Dir*>m_childDir;
};
int main()
{//(1)創建各種目錄,文件對象Dir* pdir1 = new Dir("root");//-----File* pfile1 = new File("common.mk");File* pfile2 = new File("config.mk");File* pfile3 = new File("makefile");//-----Dir* pdir2 = new Dir("app");File* pfile4 = new File("nginx.c");File* pfile5 = new File("ngx_conf.c");//-----Dir * pdir3 = new Dir("signal");File* pfile6 = new File("ngx_signal.c");//-------Dir * pdir4 = new Dir(" include");File* pfile7 = new File("ngx_func.h");File* pfile8 = new File("ngx_signal.h");//(2)構造樹形目錄結構pdir1->AddFile(pfile1);pdir1->AddFile(pfile2);pdir1->AddFile(pfile3);//-----pdir1->AddDir(pdir2);pdir2->AddFile(pfile4);pdir2->AddFile(pfile5);pdir1->AddDir(pdir3);pdir3->AddFile(pfile6); pdir1->AddDir(pdir4);pdir4->AddFile(pfile7); pdir4->AddFile(pfile8);//(3)輸出整個目錄結構,只要調用根目錄的ShowName方法即可,//每個目錄都有自己的ShowName方法負責自己的文件和目錄的顯示pdir1->ShowName("");//(4)釋放資源delete pfile8;delete pfile7;delete pdir4;//-------delete pfile6;delete pdir3;//-------delete pfile5;delete pfile4;delete pdir2;//------delete pfile3;delete pfile2;delete pfile1;delete pdir1;
}

在上述代碼中,我們通過ShowName這個函數用來顯示當前文件/目錄的名稱,這種顯示名稱的行為是否具有一致性?
對于一開始提及的需求,打印出層級結構信息,我們能否將一致性的操作提取出來呢?

如果不提取出來,那就是像上面代碼一樣,定義了兩個類,這兩個類之間的成員函數其實沒有任何關系,雖然都是叫做ShowName,但分屬于不同的類,如果共有的行為再多一點呢?比如輸出類型,ShowType這種,其實行為也是一致的。

由此引出我們的組合模式的原理闡述:

組合模式
將一組對象(文件和目錄)組織成樹形結構以表示“部分-整體”的層次結構(目錄中包含文件和子目錄)。使得用戶對單個對象(文件)和組合對象(日錄)的操作/使用/處理(遞歸遍歷并執行ShowName邏輯等)具有一致性。

在這個例子里面,File類型就是部分,Dir類型就是整體的層次結構。

2.一致性抽象處理

對于行為一致的情況,在設計模式中通常用接口進行抽象,我們再引入接口類FileSystem

#include <iostream>
#include <string>
#include <list>class FileSystem
{
public:virtual void ShowName(int level) = 0;//顯示名字,用level表示顯示的層級,用于顯示對齊virtual int Add(FileSystem* pfilesys) = 0;virtual int Remove(FileSystem* pfilesys) = 0;virtual ~FileSystem() {}
};
class File:public FileSystem
{
public:File(std::string name) :m_sname(name) {}void ShowName(int level) override{for (int i = 0; i < level; ++i) { std::cout << "  "; };std::cout << "-" << m_sname << std::endl;}int Add(FileSystem* pfilesys)override{return -1;};int Remove(FileSystem* pfilesys)override{return -1;};private:std::string m_sname;	//文件名
};class Dir :public FileSystem
{
public:Dir(std::string name) :m_sname(name) {};int Add(FileSystem* pfilesys)override{m_child.push_back(pfilesys);return 0;}int Remove(FileSystem* pDir)override{m_child.remove(pDir);return 0;}void ShowName(int level)override{for (int i = 0; i < level; ++i) { std::cout << "  "; };std::cout << "+" << m_sname << std::endl;level++;for (auto iter=m_child.begin();iter!=m_child.end();++iter){(*iter)->ShowName(level);}}
private:std::string m_sname;std::list<FileSystem*>m_child;
};
int main()
{//(1)創建各種目錄,文件對象FileSystem* pdir1 = new Dir("root");//-----FileSystem* pfile1 = new File("common.mk");FileSystem* pfile2 = new File("config.mk");FileSystem* pfile3 = new File("makefile");//-----FileSystem* pdir2 = new Dir("app");FileSystem* pfile4 = new File("nginx.c");FileSystem* pfile5 = new File("ngx_conf.c");//-----FileSystem * pdir3 = new Dir("signal");FileSystem* pfile6 = new File("ngx_signal.c");//-------FileSystem * pdir4 = new Dir(" include");FileSystem* pfile7 = new File("ngx_func.h");FileSystem* pfile8 = new File("ngx_signal.h");//(2)構造樹形目錄結構pdir1->Add(pfile1);pdir1->Add(pfile2);pdir1->Add(pfile3);//-----pdir1->Add(pdir2);pdir2->Add(pfile4);pdir2->Add(pfile5);pdir1->Add(pdir3);pdir3->Add(pfile6);pdir1->Add(pdir4);pdir4->Add(pfile7);pdir4->Add(pfile8);//(3)輸出整個目錄結構,只要調用根目錄的ShowName方法即可,//每個目錄都有自己的ShowName方法負責自己的文件和目錄的顯示pdir1->ShowName(0);//(4)釋放資源delete pfile8;delete pfile7;delete pdir4;//-------delete pfile6;delete pdir3;//-------delete pfile5;delete pfile4;delete pdir2;//------delete pfile3;delete pfile2;delete pfile1;delete pdir1;
}

在這里插入圖片描述

在上述定義中,用戶是指main主函數中的調用代碼,一致性指不用區分樹葉還是樹枝,兩者都繼承自FileSystem,都具有相同的接口,可以做相同的調用。可以看到,組合模式的設計思路其實就是用樹形結構來組織數據,然后通過在樹中遞歸遍歷各個節點并在每個節點上統一地調用某個接口(例如,都調用ShowName成員函數)或進行某些運算。總之,組合模式之所以稱為結構型模式,是因為該模式提供了一個結構,可以同時包容單個對象和組合對象。組合模式發揮作用的前提是具體數據必須能以樹形結構的方式表示,樹中包含了單個對象和組合對象。該模式專注于樹形結構中單個對象和組合對象的遞歸遍歷(只有遞歸遍歷才能體現出組合模式的價值),能把相同的操作(FileSystem 定義的接口)應用在單個以及組合對象上,并且可以忽略單個對象和組合對象之間的差別。

3.透明組合模式與安全組合模式

這里我們將一致性的操作提取出來,作為一個接口,這樣滿足了用戶對單個對象和組合對象的使用具有一致性。可是又引入了新的問題,我的文件File類型,其實不支持Add和Remove接口,沒必要支持,因為它已經是一棵樹中的葉子節點了,不能再增加和刪除節點。這種實現方式在組合模式中稱之為透明組合模式

它的優點就是保證所有的組件,都有相同的接口,確保了操作的一致性。但是缺點也很明顯,存在部分組件需要實現本來不需要的接口,這就導致了冗余,同時也會造成偶發性的錯誤調用,比如錯誤的讓File類的對象調用了Add函數,但是返回一個-1,并沒有任何用處。

針對這個缺陷,我們引入安全組合模式
在這里插入圖片描述
通過類圖,我們可以看到,我們把共有的一致性操作還是抽象出來了,但是對于只有Dir這種獨有的Add、Remove等操作,我們放到Dir自身上去定義與實現。

其實我在看書的時候就在想,既然是操作的一致性,為什么還要把Dir的一些接口比如AddFile抽象成Add,再放到FileSystem里面去,明明File沒有Add這個操作。但是看了后面的透明組合模式和安全組合模式,我也不得不說,為了引出兩個組合模式的區別吧。

最后貼一些組合模式的常用場景
(1)在公司組織結構中采用組合模式,公司組織架構如圖
在這里插入圖片描述

(2)對某個目錄下的所有文件(包括子目錄的文件)進行殺毒工作
在這里插入圖片描述
(3)利用圖元進行圖形的繪制工作
在這里插入圖片描述

總結

組合模式說的直白一點,兩句話。

用在樹形結構,存在部分與整體關系中。抽象出整體與部分的共有操作,用統一的接口實現

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

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

相關文章

48Days-Day12 | 添加字符,數組變換,裝箱問題

添加字符 添加字符_牛客筆試題_牛客網 算法原理 因為本題數據量都比較小&#xff0c;所以我們可以直接使用暴力解法&#xff0c;枚舉B字符串的每一個位置作為與A字符串比較的起點&#xff0c;維護一個最小位數的值 代碼 import java.util.*;// 注意類名必須為 Main, 不要有…

關于npm前端項目編譯時棧溢出 Maximum call stack size exceeded的處理方案

背景&#xff1a;使用vueelementui的前端項目&#xff0c;使用jenkins進行自動化編譯部署&#xff0c;某天在進行編譯發版的時候&#xff0c;突然出現 npm ERR! Maximum call stack size exceeded 錯誤&#xff0c;一直都沒法編譯成功。原因&#xff1a;隨著前端項目的不斷迭代…

微信小程序組件發布為 npm 包的具體步驟

1. 準備工作 首先&#xff0c;您需要在系統上安裝 Node.js 和 npm。如果尚未安裝&#xff0c;請訪問 Node.js — Run JavaScript Everywhere 下載并安裝最新版本。 2. 創建獨立的組件目錄 為了更好地管理組件&#xff0c;建議將其從當前項目中獨立出來&#xff1a; wechat-…

LCM中間件入門(2):LCM核心實現原理解析

文章目錄一、good()函數&#xff1a;LCM實例狀態檢查的實現原理1. 實現邏輯2. 簡化代碼示例&#xff08;C語言核心邏輯&#xff09;二、publish()&#xff1a;向指定channel發送消息的原理1. 完整流程拆解2. 簡化代碼示例&#xff08;C核心邏輯&#xff09;三、subscribe()&…

Nginx安裝及配置

一.nginx安裝1.1nginx概述1.1.1 nginx介紹Nginx是一款高性能的開源HTTP和反向代理服務器&#xff0c;是免費的、開源的、高性能的HTTP和反向代理服務器、郵件代理服務器、以及TCP/UDP代理服務器解決C10K問題&#xff08;10K Connections&#xff09;。同時也支持IMAP/POP3代理服…

SelectDB數據庫,新一代實時數據倉庫的全面解析與應用

摘要&#xff1a;SelectDB是一款基于Apache Doris的新一代實時數據倉庫解決方案&#xff0c;具備實時極速、融合統一、彈性架構和開放生態四大核心特性。它采用云原生存算分離架構&#xff0c;支持秒級數據更新、毫秒級查詢響應&#xff0c;在TPC-H等基準測試中性能超越傳統系統…

自動駕駛的未來:多模態傳感器鉆機

倫敦大學學院博士生袁方正在建造多模態傳感器鉆機&#xff0c;以探索自動駕駛的未來。他的最新設置匯集了一套尖端傳感器&#xff1a; &#x1f4e1; 60 GHz 雷達&#xff08;用于 Raspberry Pi 的 DreamHAT&#xff09;DreamRF &#x1f4f7; RGB 深度攝像頭 &#xff08;Real…

13.Redis 的級聯復制

Redis 的級聯復制 即實現基于Slave節點的Slave 1. 修改 Slave 節點配置文件 # 第一個slave節點 [rootubuntu2204 ~]#vim /apps/redis/etc/redis.conf(大約在533行附近) replicaof 10.0.0.100 6379 masterauth 123456# 第二個slave節點 [rootubuntu2204 ~]#vim /apps/redis/etc/…

spring-ai-alibaba 學習(二十)——graph之檢查點

前面學習了graph的基本概念&#xff0c;參數設置&#xff0c;特殊節點和邊&#xff0c;今天學習一下檢查點檢查點可能名稱比較抽象&#xff0c;換個名字可能比較容易理解&#xff0c;進度保存點或者存檔點&#xff0c;可以類比游戲中保存當前游戲進度的存檔進度主要用于人工介入…

sqli-labs:Less-19關卡詳細解析

1. 思路&#x1f680; 本關的SQL語句為&#xff1a; $insert"INSERT INTO security.referers (referer, ip_address) VALUES ($uagent, $IP)";注入類型&#xff1a;字符串型&#xff08;單引號包裹&#xff09;、INSERT操作提示&#xff1a;參數需以閉合關鍵參數&a…

Java小紅書源碼1:1還原uniapp_仿小紅書源碼

在內容驅動型社交平臺興起的背景下&#xff0c;小紅書作為圖文/視頻種草社區的代表&#xff0c;其產品結構與功能體驗逐漸成為眾多開發者與創業團隊的模仿藍本。本項目基于Java后端uni-app前端棧&#xff0c;完整復刻小紅書主要功能&#xff0c;支持多端&#xff08;小程序、H5…

USB Type-C PD協議一文通

原文&#xff1a;https://www.richtek.com/Design%20Support/Technical%20Document/AN056?sc_langzh-TW譯者&#xff1a;TrustZone1、概述 USB Type-C標準的出現是為了滿足不斷增長的現代設備之間的連接需要&#xff0c;它在傳統USB標準的基礎上提供了更高的電源傳輸能力和資料…

AI文檔比對和Word的“比較”功能有什么區別?

AI文檔比對工具的核心區別在于&#xff0c;它超越了Word的純文本“找不同”&#xff0c;能精準處理掃描件、表格及印章&#xff0c;并將文檔審查從被動的文本核對&#xff0c;處理大文檔也更為快速及準確。 為什么Word的“比較”功能已經不夠用了&#xff1f; 對于許多專業人士…

AI驅動SEO關鍵詞智能進化

內容概要 隨著人工智能&#xff08;AI&#xff09;技術的快速演進&#xff0c;搜索引擎優化&#xff08;SEO&#xff09;領域正迎來前所未有的變革。本文核心探討AI如何驅動SEO關鍵詞的智能進化&#xff0c;重點解析人工智能革新關鍵詞研究與優化策略的機制&#xff0c;包括智能…

基于SpringBoot+MyBatis+MySQL+VUE實現的青年公寓服務平臺管理系統(附源碼+數據庫+畢業論文+部署教程+配套軟件)

摘 要 傳統信息的管理大部分依賴于管理人員的手工登記與管理&#xff0c;然而&#xff0c;隨著近些年信息技術的迅猛發展&#xff0c;讓許多比較老套的信息管理模式進行了更新迭代&#xff0c;房屋信息因為其管理內容繁雜&#xff0c;管理數量繁多導致手工進行處理不能滿足廣…

12.Redis 主從復制

Redis 主從復制Redis 主從復制1. Redis 主從復制架構2. 主從復制實現2.1 主從命令配置2.1.1 啟用主從同步2.1.2 查看日志觀察同步狀態2.1.3 修改 Slave 節點配置文件2.1.4 刪除主從同步3. 主從復制故障恢復3.1 Slave 節點故障和恢復3.2 Master 節點故障和恢復3.3 常見主從復制故…

微服務的編程測評系統8-題庫管理-競賽管理

提示&#xff1a;文章寫完后&#xff0c;目錄可以自動生成&#xff0c;如何生成可參考右邊的幫助文檔 文章目錄前言1. 添加題目1.1 service方法1.2 畫頁面-引入富文本和代碼編輯框1.3 子組件中發送請求2. 獲取題目詳情3. 編輯題目4. 刪除題目5. Vue生命周期函數5.1 創建階段5.2…

基于springboot的學習輔導系統設計與實現

學生&#xff1a;注冊登錄&#xff0c;學習視頻&#xff0c;學習資料&#xff0c;在線交流&#xff0c;系統公告&#xff0c;個人中心&#xff0c;后臺管理教師&#xff1a;登錄&#xff0c;個人中心&#xff0c;學習視頻管理&#xff0c;學習資料管理&#xff0c;簽到記錄管理…

Kubernetes (K8s) 部署Doris

官網提供yaml地址下載部署 https://doris.apache.org/zh-CN/docs/2.0/install/cluster-deployment/k8s-deploy/install-env/禁用和關閉 swap 在部署 Doris 時&#xff0c;建議關閉 swap 分區。 通過以下命令可以永久關閉 swap 分區。 echo "vm.swappiness 0">>…

AI生成圖片工具分享!

CZL在線工具箱近日推出了一款基于Cloudflare Workers AI的免費在線AI圖片生成服務。該服務采用**Stable Diffusion XL&#xff08;SDXL&#xff09;**模型&#xff0c;為用戶提供高質量、逼真的圖像生成體驗。 核心特性 全球GPU網絡&#xff1a;基于Cloudflare全球分布式GPU網…