設計模式【11】-- 搞定組合模式
開局還是那種圖,各位客官往下看...
組合模式是什麼?
組合模式,將物件組合成樹形結構以表示“部分-整體”的層次結構。(百度百科)
其實,組合模式,又稱為部分整體模式,用於把一組相似的物件當作一個單一的物件。組合模式依據樹形結構來組合物件,用來表示部分以及整體層次。這種型別的設計模式屬於結構型模式,它建立了物件組的樹形結構。
關鍵字: 一致性
, 整體
, 部分
比如公司的組織架構,就是樹形的結構:
公司下面有部門與人,人是屬於部門,部門可以擁有子部門,如果我們將上面的節點,不管是組織,還是人,統一抽象成為一個 node
,那麼,我們並不需要關心當前節點到底是人,還是部門,統計人數的時候或者遍歷的時候,一視同仁。
還有就是 Java Swing
程式設計中,一般也會容器的說法: Container
,我們在 Container
裡面可以放子的容器,也可以放具體的元件,比如 Button
或者 Checkbox
,其實這也是一種部分-整體的思維。
除此之外,最經典的是資料夾與檔案的表示,一個資料夾(容器物件)既可以存放資料夾(容器物件),也可以存放檔案(葉子物件)。如果把樹的每個節點攤平,那就是 List
。而樹結構,則是更能直觀的體現每個節點與整體的關係。
為什麼需要這個模式呢?它的目的是什麼?
主要是想要對外提供一致性的使用方式,即使容器物件與葉子物件之間屬性差別可能非常大,我們希望抽象出相同的地方,一致的處理。
組合模式的角色
組合模式中一般有以下三種角色:
Component Leaf Composite
注意:關鍵點就是抽象構件,所有節點都統一,不再需要呼叫者關心葉子節點與非葉子節點的差異。
組合模式的兩種實現
組合模式有兩種不同的實現,分別是 透明模式 和 安全模式 :
兩者的區別在於透明模式將組合使用的方法放到抽象類中,而安全模式則是放到具體實現類中
透明模式
透明模式是把組合的方法抽象到抽象類中,不管是葉子節點,還是組合節點,都有一樣的方法,這樣對外處理的時候是一致的,不過實際上有些方法對葉子節點而言,是沒有用的,有些累贅。
下面是程式碼實現:
抽象類,要求實現三個方法,增加,刪除,展示:
package designpattern.composite; public abstract class Component { String name; public Component(String name) { this.name = name; } public abstract void add(Component component); public abstract void remove(Component component); public abstract void show(int depth); }
組合類:
import java.util.ArrayList; import java.util.List; public class Composite extends Component { List<Component> childs = new ArrayList<>(); public Composite(String name) { super(name); } @Override public void add(Component component) { this.childs.add(component); } @Override public void remove(Component component) { this.childs.remove(component); } @Override public void show(int depth) { for (int i = 0; i < depth; i++) { System.out.print(" "); } System.out.println(name + ": "); for (Component component : childs) { component.show(depth + 1); } } }
葉子類:
public class Leaf extends Component { public Leaf(String name) { super(name); } @Override public void add(Component component) { } @Override public void remove(Component component) { } @Override public void show(int depth) { for (int i = 0; i < depth; i++) { System.out.print(" "); } System.out.println(name); } }
測試類:
public class Test { public static void main(String[] args) { Composite folderRoot = new Composite("備忘錄資料夾"); folderRoot.add(new Leaf("word 檔案")); folderRoot.add(new Leaf("ppt 檔案")); Composite folderLevel1 = new Composite("週報資料夾"); folderLevel1.add(new Leaf("20210101週報")); folderRoot.add(folderLevel1); Composite folderLevel2 = new Composite("筆記資料夾"); folderLevel2.add(new Leaf("jvm.ppt")); folderLevel2.add(new Leaf("redis.txt")); folderLevel1.add(folderLevel2); folderRoot.add(new Leaf("需求.txt")); Leaf leaf = new Leaf("bug單.txt"); folderRoot.add(leaf); folderRoot.remove(leaf); folderRoot.show(0); } }
執行結果如下:
備忘錄資料夾: word 檔案 ppt 檔案 週報資料夾: 20210101週報 筆記資料夾: jvm.ppt redis.txt 需求.txt
可以看到以上是一棵樹的結果,不管是葉子節點,還是組合節點,都是一樣的操作。
安全模式
安全模式,就是葉子節點和組合節點的特性分開,只有組合節點才有增加和刪除操作,而兩者都會擁有展示操作。但是如果同時對外暴露葉子節點和組合節點的話,使用起來還需要做特殊的判斷。
抽象元件:
public abstract class Component { String name; public Component(String name) { this.name = name; } public abstract void show(int depth); }
元件構件:
public class Composite extends Component { List<Component> childs = new ArrayList<>(); public Composite(String name) { super(name); } public void add(Component component) { this.childs.add(component); } public void remove(Component component) { this.childs.remove(component); } @Override public void show(int depth) { for (int i = 0; i < depth; i++) { System.out.print(" "); } System.out.println(name + ": "); for (Component component : childs) { component.show(depth + 1); } } }
葉子節點:
public class Leaf extends Component { public Leaf(String name) { super(name); } @Override public void show(int depth) { for (int i = 0; i < depth; i++) { System.out.print(" "); } System.out.println(name); } }
測試類不變,測試結果也一樣:
備忘錄資料夾: word 檔案 ppt 檔案 週報資料夾: 20210101週報 筆記資料夾: jvm.ppt redis.txt 需求.txt
安全模式中,葉子節點沒有多餘的方法,沒有空的方法,外面呼叫的時候,不會呼叫到空方法。但是需要對節點進行判斷,才能知道哪一個方法能調,哪一個方法不能調。
小結一下
組合模式的優點:
- 可以分層次定義複雜物件,表示區域性和全部,客戶端可以忽略不同的節點的差異。
- 從高層次呼叫,可以很順暢的呼叫到每一個區域性,一致性比較強。
- 節點自由搭配,靈活度比較高。
缺點:
- 在使用組合模式時,其葉子和組合節點的宣告都是實現類,而不是介面,違反了依賴倒置原則。
使用場景:
-
希望忽略每個部分的差異,客戶端一致使用
-
需要表現為樹形結構,以表示“整體-部分”的結構層次。
以一句網友的話結尾:
“張無忌學太極拳,忘記了所有招式,打倒了"玄冥二老",所謂"心中無招"。設計模式可謂招數,如果先學通了各種模式,又忘掉了所有模式而隨心所欲,可謂OO之最高境界。”
【作者簡介】:
秦懷,公眾號【 秦懷雜貨店 】作者,個人網站: http://aphysia.cn,技術之路不在一時,山高水長,縱使緩慢,馳而不息。
設計模式系列:
- 設計模式【1】-- 單例模式到底幾種寫法?
- 設計模式【1.1】-- 你想如何破壞單例模式?
- 設計模式【1.2】-- 列舉式單例有那麼好用麼?
- 設計模式【1.3】-- 為什麼餓漢式單例是執行緒安全的?
- 設計模式【2】-- 簡單工廠模式瞭解一下?
- 設計模式【2.1】-- 簡單工廠模式怎麼演變成工廠方法模式?
- 設計模式【2.2】-- 工廠模式怎麼演變成抽象工廠模式?
- 設計模式【3.1】-- 淺談代理模式之靜態、動態、cglib代理
- 設計模式【3.2】-- JDK動態代理原始碼分析有多香?
- 設計模式【3.3】-- CGLIB動態代理原始碼解讀
- 設計模式【4】-- 建造者模式詳解
- 設計模式【5】-- 原型模式
- 設計模式【6.1】-- 初探介面卡模式
- 設計模式【6.2】-- 再聊聊介面卡模式
- 設計模式【7】-- 探索一下橋接模式
- 設計模式【8】-- 手工耿教我寫裝飾器模式
- 設計模式【9】-- 外觀模式?沒那麼高大上
- 設計模式【10】-- 順便看看享元模式
- 設計模式【15】--從審批流中學習責任鏈模式
- 設計模式【14】-- 從智慧音箱中學習命令模式
- 設計模式【12】-- 搞定最近大火的策略模式
- 設計模式【11】-- 搞定組合模式
- 萬字長文帶你漫遊資料結構世界
- 萬字長文帶你漫遊資料結構世界
- java集合【13】——— Stack原始碼分析走一波
- 設計模式【10】-- 順便看看享元模式
- 設計模式【7】-- 探索一下橋接模式
- 完蛋,我的事務怎麼不生效?
- 無快不破,在本地 docker 執行 IDEA 裡面的專案?
- 設計模式【5】-- 原型模式
- 設計模式【3.2】-- JDK動態代理原始碼分析有多香?
- 馬拉車演算法,其實並不難!!!
- Mybatis【9】-- Mybatis佔位符#{}和拼接符${}有什麼區別?
- Mybatis【8】-- Mybatis返回List或者Map以及模糊查詢怎麼搞?
- Lambda【1】-- List相關Lambda表示式使用(上篇)
- Mybatis【7】-- Mybatis如何知道增刪改是否成功執行?
- JVM筆記【1】-- 執行時資料區
- 設計模式【1.1】-- 你想如何破壞單例模式?