安裝Harbor並修改預設使用的80埠
組合模式(Composite Pattern)是一種結構型設計模式, 你可以使用它將物件組合成樹狀結構, 並且能像使用獨立物件一樣使用它們。組合模式建立了一個包含自己物件組的類。該類提供了修改相同物件組的方式。
組合模式 問題引入
如果應用的核心模型能用樹狀結構表示, 在應用中使用組合模式才有價值。
例如, 你有兩類物件:產品
和盒子
。 一個盒子中可以包含多個 產品或者幾個較小的 盒子 。 這些小 盒子中同樣可以包含一些 產品或更小的 盒子 , 以此類推。
假設你希望在這些類的基礎上開發一個定購系統。 訂單中可以包含無包裝的簡單產品, 也可以包含裝滿產品的盒子……以及其他盒子。 此時你會如何計算每張訂單的總價格呢?
你可以嘗試直接計算: 開啟所有盒子, 找到每件產品, 然後計算總價。 這在真實世界中或許可行, 但在程式中, 你並不能簡單地使用迴圈語句來完成該工作。 你必須事先知道所有產品
和盒子
的類別, 所有盒子的巢狀層數以及其他繁雜的細節資訊。 因此, 直接計算極不方便, 甚至完全不可行。
組合模式 解決方案
組合模式建議使用一個通用介面來與產品
和盒子
進行互動, 並且在該介面中宣告一個計算總價的方法。
那麼方法該如何設計呢? 對於一個產品, 該方法直接返回其價格; 對於一個盒子, 該方法遍歷盒子中的所有專案, 詢問每個專案的價格, 然後返回該盒子的總價格。 如果其中某個專案是小一號的盒子, 那麼當前盒子也會遍歷其中的所有專案, 以此類推, 直到計算出所有內部組成部分的價格。 你甚至可以在盒子的最終價格中增加額外費用, 作為該盒子的包裝費用。
該方式的最大優點在於你無需瞭解構成樹狀結構的物件的具體類。 你也無需瞭解物件是簡單的產品還是複雜的盒子。 你只需呼叫通用介面以相同的方式對其進行處理即可。 當你呼叫該方法後, 物件會將請求沿著樹結構傳遞下去。
真實世界類比
大部分國家的軍隊都採用層次結構管理。 每支部隊包括幾個師, 師由旅構成, 旅由團構成, 團可以繼續劃分為排。 最後, 每個排由一小隊實實在在的士兵組成。 軍事命令由最高層下達, 通過每個層級傳遞, 直到每位士兵都知道自己應該服從的命令。
組合模式 結構
- 元件 (Component) 介面描述了樹中簡單專案和複雜專案所共有的操作。
-
葉節點 (Leaf) 是樹的基本結構, 它不包含子專案。
一般情況下, 葉節點最終會完成大部分的實際工作, 因為它們無法將工作指派給其他部分。 -
容器 (Container)——又名 “組合 (Composite)”——是包含葉節點或其他容器等子專案的單位。 容器不知道其子專案所屬的具體類, 它只通過通用的元件介面與其子專案互動。
容器接收到請求後會將工作分配給自己的子專案, 處理中間結果, 然後將最終結果返回給客戶端。
-
客戶端 (Client) 通過元件介面與所有專案互動。 因此, 客戶端能以相同方式與樹狀結構中的簡單或複雜專案互動。
組合模式 虛擬碼
在本例中, 我們將藉助組合模式幫助你在圖形編輯器中實現一系列的幾何圖形。
組合圖形Compoundraphic
是一個容器, 它可以由多個包括容器在內的子圖形構成。 組合圖形與簡單圖形擁有相同的方法。 但是, 組合圖形自身並不完成具體工作, 而是將請求遞迴地傳遞給自己的子專案, 然後 “彙總” 結果。
通過所有圖形類所共有的介面, 客戶端程式碼可以與所有圖形互動。 因此, 客戶端不知道與其互動的是簡單圖形還是組合圖形。 客戶端可以與非常複雜的物件結構進行互動, 而無需與組成該結構的實體類緊密耦合。
1 // 元件介面會宣告組合中簡單和複雜物件的通用操作。 2 interface Graphic is 3 method move(x, y) 4 method draw() 5 6 // 葉節點類代表組合的終端物件。葉節點物件中不能包含任何子物件。葉節點物件 7 // 通常會完成實際的工作,組合物件則僅會將工作委派給自己的子部件。 8 class Dot implements Graphic is 9 field x, y 10 11 constructor Dot(x, y) { ... } 12 13 method move(x, y) is 14 this.x += x, this.y += y 15 16 method draw() is 17 // 在座標位置(X,Y)處繪製一個點。 18 19 // 所有元件類都可以擴充套件其他元件。 20 class Circle extends Dot is 21 field radius 22 23 constructor Circle(x, y, radius) { ... } 24 25 method draw() is 26 // 在座標位置(X,Y)處繪製一個半徑為 R 的圓。 27 28 // 組合類表示可能包含子專案的複雜元件。組合物件通常會將實際工作委派給子項 29 // 目,然後“彙總”結果。 30 class CompoundGraphic implements Graphic is 31 field children: array of Graphic 32 33 // 組合物件可在其專案列表中新增或移除其他元件(簡單的或複雜的皆可)。 34 method add(child: Graphic) is 35 // 在子專案陣列中新增一個子專案。 36 37 method remove(child: Graphic) is 38 // 從子專案陣列中移除一個子專案。 39 40 method move(x, y) is 41 foreach (child in children) do 42 child.move(x, y) 43 44 // 組合會以特定的方式執行其主要邏輯。它會遞迴遍歷所有子專案,並收集和 45 // 彙總其結果。由於組合的子專案也會將呼叫傳遞給自己的子專案,以此類推, 46 // 最後組合將會完成整個物件樹的遍歷工作。 47 method draw() is 48 // 1. 對於每個子部件: 49 // - 繪製該部件。 50 // - 更新邊框座標。 51 // 2. 根據邊框座標繪製一個虛線長方形。 52 53 54 // 客戶端程式碼會通過基礎介面與所有元件進行互動。這樣一來,客戶端程式碼便可同 55 // 時支援簡單葉節點元件和複雜元件。 56 class ImageEditor is 57 method load() is 58 all = new CompoundGraphic() 59 all.add(new Dot(1, 2)) 60 all.add(new Circle(5, 3, 10)) 61 // ... 62 63 // 將所需元件組合為複雜的組合元件。 64 method groupSelected(components: array of Graphic) is 65 group = new CompoundGraphic() 66 group.add(components) 67 all.remove(components) 68 all.add(group) 69 // 所有元件都將被繪製。 70 all.draw()
組合模式 適用場景
如果你需要實現樹狀物件結構, 可以使用組合模式。
組合模式為你提供了兩種共享公共介面的基本元素型別: 簡單葉節點和複雜容器。 容器中可以包含葉節點和其他容器。 這使得你可以構建樹狀巢狀遞迴物件結構。
如果你希望客戶端程式碼以相同方式處理簡單和複雜元素, 可以使用該模式。
組合模式中定義的所有元素共用同一個介面。 在這一介面的幫助下, 客戶端不必在意其所使用的物件的具體類。
組合模式 實現方式
- 用的核心模型能夠以樹狀結構表示。 嘗試將其分解為簡單元素和容器。 記住, 容器必須能夠同時包含簡單元素和其他容器。
-
定義介面及其一系列方法, 這些方法對簡單和複雜元素都有意義。
-
個葉節點類表示簡單元素。 程式中可以有多個不同的葉節點類。
-
容器類表示複雜元素。 在該類中, 建立一個數組成員變數來儲存對於其子元素的引用。 該陣列必須能夠同時儲存葉節點和容器, 因此請確保將其宣告為組合介面型別。
件介面方法時, 記住容器應該將大部分工作交給其子元素來完成。
-
在容器中定義新增和刪除子元素的方法。
記住, 這些操作可在元件介面中宣告。 這將會違反介面隔離原則, 因為葉節點類中的這些方法為空。 但是, 這可以讓客戶端無差別地訪問所有元素, 即使是組成樹狀結構的元素。
組合模式 優缺點
組合模式 優點
- 你可以利用多型和遞迴機制更方便地使用複雜樹結構。
-
開閉原則。 無需更改現有程式碼, 你就可以在應用中新增新元素, 使其成為物件樹的一部分。
組合模式 缺點
- 對於功能差異較大的類, 提供公共介面或許會有困難。 在特定情況下, 你需要過度一般化元件介面, 使其變得令人難以理解。
組合模式 與其他模式的關係
-
橋接模式、 狀態模式和策略模式 (在某種程度上包括介面卡模式) 模式的介面非常相似。 實際上, 它們都基於組合模式——即將工作委派給其他物件, 不過也各自解決了不同的問題。 模式並不只是以特定方式組織程式碼的配方, 你還可以使用它們來和其他開發者討論模式所解決的問題。
-
責任鏈模式通常和組合模式結合使用。 在這種情況下, 葉元件接收到請求後, 可以將請求沿包含全體父元件的鏈一直傳遞至物件樹的底部。
-
你可以使用迭代器模式來遍歷組合樹。
-
你可以使用訪問者模式對整個組合樹執行操作。
-
你可以使用享元模式實現組合樹的共享葉節點以節省記憶體。
-
組合和裝飾模式的結構圖很相似, 因為兩者都依賴遞迴組合來組織無限數量的物件。
裝飾類似於組合, 但其只有一個子元件。 此外還有一個明顯不同: 裝飾為被封裝物件添加了額外的職責, 組合僅對其子節點的結果進行了 “求和”。
但是, 模式也可以相互合作: 你可以使用裝飾來擴充套件組合樹中特定物件的行為。
-
大量使用組合和裝飾的設計通常可從對於原型模式的使用中獲益。 你可以通過該模式來複制複雜結構, 而非從零開始重新構造。
轉載 https://geek-docs.com/design-pattern/composite-pattern/composite-pattern-index.html