設計模式之美:Composite(組合)
索引
意圖
將物件組合成樹形結構以表示 “部分-整體” 的層次結構。
Composite 使得使用者對於單個物件和組合物件的使用具有一致性。
Compose objects into tree structures to represent part-whole hierarchies.
Composite lets clients treat individual objects and compositions of objects uniformly.
結構
典型的 Composite 物件結構:
參與者
Component
- 為組合中的物件宣告介面。
- 在適當的情況下,實現所有類共有介面的預設行為
- 宣告一個介面用於訪問和管理 Component 的子元件。
- 在遞迴結構中定義一個介面,用於訪問一個父部件,並在合適的情況下實現它。
Leaf
- 在組合中表示葉節點物件,葉節點沒有子節點。
- 在組合中定義圖元物件的行為。
Composite
- 定義有子部件的那些部件的行為。
- 在 Composite 介面中實現與子部件有關的操作。
Client
- 通過 Component 介面操縱組合部件的物件。
適用性
在以下情況下可以使用 Composite 模式:
- 你想表示物件的 “部分-整體” 層次結構。
- 你希望使用者忽略組合物件與單個物件的不同,使用者將統一地使用組合結構中的所有物件。
缺點
- 與類層次結構設計原則衝突
Composite 模式的目的之一是使得使用者不知道它們正在使用具體的 Leaf 和 Composite 類。
為達到這一目的,Component 需要為 Leaf 和 Composite 定義一些公共操作,並提供預設的實現,而 Leaf 和 Composite 子類可以對它們進行重定義。
然而,這個目標會與類層次結構設計原則衝突,該原則規定:一個類只能定義那些對它的子類有意義的操作。
效果
- 定義了包含基本物件和組合物件的類層次結構。
- 簡化客戶程式碼。
- 使得更容易增加新型別的元件。
- 使你的設計變得更加一般化。
相關模式
- Command 模式描述瞭如何用一個 MacroCommand Composite 類組成一些 Command 物件,並對它們進行排序。
- 通常 “部件-父部件” 連線用於 Responsibility of Chain 模式。
- Decorator 模式經常與 Composite 模式一起使用。它們通常有一個公共的父類。
- Flyweight 讓你共享元件,但不再能引用它們的父部件。
- Iterator 可以用來遍歷 Composite。
- Visitor 將本來應該分佈在 Composite 和 Leaf 類中的操作和行為區域性化。
實現
實現方式(一):在 Component 中定義公共介面以保持透明性但損失安全性。
在 Component 中定義 Add 和 Remove 操作需要考慮安全性和透明性。
在類層次結構的根部定義子節點管理介面的方法具有良好的透明性,但是這一方法是以安全性為代價的,因為客戶有可能會做一些無意義的事情,例如在 Leaf 中 Add 物件等。
在 Composite 類中定義管理子部件的方法具有良好的安全性,但是這又損失了透明性,因為 Leaf 和 Composite 具有不同的介面。
1 namespace CompositePattern.Implementation1 2 { 3 public abstract class Component 4 { 5 protected List<Component> _children = new List<Component>(); 6 7 public abstract void Operation(); 8 9 public virtual void Add(Component component) 10 { 11 _children.Add(component); 12 } 13 14 public virtual void Remove(Component component) 15 { 16 _children.Remove(component); 17 } 18 19 public virtual IEnumerable<Component> GetChildren() 20 { 21 return _children; 22 } 23 } 24 25 public class Leaf : Component 26 { 27 public override void Operation() 28 { 29 // do something 30 } 31 32 public override void Add(Component component) 33 { 34 throw new InvalidOperationException(); 35 } 36 37 public override void Remove(Component component) 38 { 39 throw new InvalidOperationException(); 40 } 41 42 public override IEnumerable<Component> GetChildren() 43 { 44 throw new InvalidOperationException(); 45 } 46 } 47 48 public class Composite : Component 49 { 50 public override void Operation() 51 { 52 foreach (var child in _children) 53 { 54 child.Operation(); 55 } 56 // may do something 57 } 58 } 59 60 public class Client 61 { 62 public void TestCase1() 63 { 64 Component component1 = new Leaf(); 65 Component component2 = new Composite(); 66 67 component2.Add(component1); 68 69 component1.Operation(); 70 component2.Operation(); 71 } 72 } 73 }
《設計模式之美》為 Dennis Gao 釋出於部落格園的系列文章,任何未經作者本人同意的人為或爬蟲轉載均為耍流氓。
相關推薦
設計模式之美:Composite(組合)
索引 意圖 將物件組合成樹形結構以表示 “部分-整體” 的層次結構。 Composite 使得使用者對於單個物件和組合物件的使用具有一致性。 Compose objects into tree structures to represent part-whole hierarchies. Co
C++設計模式之八:Composite(組合)
一、意圖: 將物件組合成樹形結構以表示成“部分-整體”的層次結構; 二、類圖: 三、組成元素: Component:頂層介面,用於訪問子元件; Leaf:葉節點,沒有子節點; Composite:用於儲存子節點; 四、程式碼實現: #include <
設計模式之軟體開發原則(1)開閉原則和依賴倒置原則
開閉原則 定義 所謂開閉原則就是一個軟體實體如類、模組和函式應該對擴充套件開放、對修改關閉。 強呼叫抽象構建框架,實現實現拓展細節。 有優點是提高軟體的複用性和易維護展性。是面向物件的最基本原則。 依賴倒置原則 定義 高層模組不應該依賴底層模組,二者都應該依賴其抽象。 抽象不應該依賴細節:細節應該
設計模式之美學習(八):為何說要多用組合少用繼承?如何決定該用組合還是繼承?
在面向物件程式設計中,有一條非常經典的設計原則,那就是:組合優於繼承,多用組合少用繼承。為什麼不推薦使用繼承?組合相比繼承有哪些優勢?如何判斷該用組合還是繼承? 為什麼不推薦使用繼承? 繼承是面向物件的四大特性之一,用來表示類之間的 is-a 關係,可以解決程式碼複用的問題。雖然繼承有諸多作用,但繼承層次過深
設計模式之八:組合模式(Composite Pattern)
數據結構 log ide ase 統一 etc 方法 可能 模式 什麽是組合模式呢?簡單來說組合模式就是將對象合成樹形結構以表示“部分整體”的層次結構,組合模式使用戶對單個對象和組合對象使用具有一致性。 組合模式(Composite Pattern)有時
設計模式之美學習(九):業務開發常用的基於貧血模型的MVC架構違背OOP嗎?
我們都知道,很多業務系統都是基於 MVC 三層架構來開發的。實際上,更確切點講,這是一種基於貧血模型的 MVC 三層架構開發模式。 雖然這種開發模式已經成為標準的 Web 專案的開發模式,但它卻違反了面向物件程式設計風格,是一種徹徹底底的面向過程的程式設計風格,因此而被有些人稱為反模式(anti-patter
設計模式之三:單例模式(餓漢式與懶漢式)
//保證類在記憶體中只有一個物件 package com.xjh.demo.designpattern.pattern3; public class Student { private Student(){ } //懶漢式 priva
設計模式之九:迭代器模式(Iterator Pattern)
GOF 在《設計模式》:提供一種方法順序訪問一個聚合物件中的各個元素,而又不暴露該物件的內部表示。 迭代器模式 是物件行為模式。 聚合:是指一組物件的組合結構,比如:java 中的集合,陣列等。思想:迭代模式的關鍵思想就是把聚合物件的遍歷個訪問從聚合物件中分離出來,放入單
大話設計模式之四:1~5章(簡單工廠模式 、策略模式、單一職責原則、開放封閉原則 、依賴倒轉原則)
注:《大話設計模式》這本書很好的介紹了設計模式,其對應的原始碼是C#語言寫得,跑在visual studio上,所以自己先安裝visual studio ,然後將原始碼跑一跑,這樣能深刻的理解《大話設
設計模式之迭代器與組合模式(一)
很高興,這本書總共13章,這次已經是到第9章了;同時也很遺憾,小編脫離了書本,還是不知道如何描述一個設計模式。就比如迭代器與組合模式,原書篇幅比較長,小編儘量通俗易懂些,不到之處,還請各位小夥伴參考原書,小編也歡迎和大家一起交流。 有許多種方法可以把物件堆起來成為一個集合(collection)。你可以把它們
設計模式之迭代器與組合模式(二)
在上次的文章中,我們通過層層引導,已經知道了迭代器模式的由來。現在我們再好好總結下。 關於迭代器模式,你所需要知道的第一件事情,就是它依賴於一個名為迭代器的介面。這是一個可能的迭代器的介面: 現在,我們一旦有了這個介面,就可以為各種物件集合實現迭代器:陣列、列表、散列表...如果我麼想要為陣列實現迭代器,
設計模式之迭代器與組合模式(三)
現在我們已經能愉快地看著一頁一頁羅列出來的選單進行點菜了。現在又有的小夥伴希望能夠加上一份餐後甜點的“子選單”。怎麼辦呢?我們不僅僅要支援多個選單,甚至還要支援選單中的選單。 如果我們能讓甜點選單變成餐廳選單集合的一個元素,那該有多好。但是根據現在的實現,根本做不到呀。我們想要的是這樣的: 我們需要什麼
設計模式之迭代器與組合模式(四)
因為這系列篇幅較長,所以在這裡也不進行任何鋪墊,直奔主題去啦。 利用組合設計選單 我們要如何在選單上應用組合模式呢?一開始,我們需要建立一個元件介面來作為選單和選單項的共同介面,讓我們能夠用統一的做法來處理選單和選單項。換句話說,我們可以針對選單或選單項呼叫相同的方法。 讓我們從頭來看看如何讓選單能夠符合組合
設計模式之六:工廠方法模式(Factory method Pattern)
image bsp turn stat ole ati ace 方法 系統擴展 工廠方法(Factory Method)模式就是定義一個創建對象的工廠接口,將實際創建工作推遲到子類當中。 核心工廠類不再負責具體產品的創建,僅提供了具體工廠子類必須實現的接口,這樣核
C#設計模式之9:模板方法
like not 存在 als col wan 結構 允許 封裝 模板方法 模板方法是一個方法,定義了算法的步驟,並允許子類為一個或多個步驟提供實現。 本例中用沖泡咖啡和茶的例子來說明: 上圖說明了沖泡咖啡和茶的步驟,可以看出沖泡咖啡和茶的步驟差不多,很相似,先來看看沒有
設計模式之觀察者-Java(簡單例子)
1、定義:源於GOF的Design Patterns一書。 Define a one-to-many dependency between objects so that when oneobject changes state, all its dependents are notifie
設計模式之二:工廠方法模式
定義 工廠方法模式又稱為工廠模式,也叫虛擬構造器(Virtual Constructor)模式或者多型工廠模式(Polymorphic Factory),在工廠方法模式中,父類負責定義建立物件的公共介面,而子類則負責生成具體的物件,這樣做的目的是將類的例項化操作延遲到子類中完
《大話設計模式》Java程式碼示例(三)之裝飾模式
裝飾模式(Decorator):動態地給一個物件新增一些額外的職責,就增加功能來說,裝飾模式比生成子類更為靈活。 package decorator; /** * 裝飾模式(Decorator) * Person類 */ public class Perso
《大話設計模式》Java程式碼示例(四)之代理模式
代理模式(Proxy):為其他物件提供一種代理以控制對這個物件的訪問。 package proxy; /** * 代理模式(Proxy) * 被追求者類 */ public class SchoolGirl { private String nam
《大話設計模式》Java程式碼示例(五)之工廠方法模式
工廠方法模式(Factory Method):定義一個用於建立物件的介面,讓子類決定例項化哪一個類,工廠方法使一個類的例項化延遲到其子類。 package factorymethod; /** * 工廠方法模式(Factory Method) * 雷鋒類 */