模式動機
對於樹形結構,當容器對象(如文件夾)的某一個方法被調用時,將遍歷整個樹形結構,尋找也包含這個方法的成員對象(可以是容器對象,也可以是葉子對象,如子文件夾和文件)並調用執行。(遞歸調用)
由於容器對象和葉子對象在功能上的區別,在使用這些對象的客戶端代碼中必須有區別地對待容器對象和葉子對象,而實際上大多數情況下客戶端希望一致地處理它們,因為對於這些對象的區別對待將會使得程序非常復雜。
組合模式描述了如何將容器對象和葉子對象進行遞歸組合,使得用戶在使用時無須對它們進行區分,可以一致地對待容器對象和葉子對象,這就是組合模式的模式動機。
模式定義
組合模式(Composite Pattern):組合多個對象形成樹形結構以表示“整體-部分”的結構層次。組合模式對單個對象(即葉子對象)和組合對象(即容器對象)的使用具有一致性。
組合模式又可以稱為“整體-部分”(Part-Whole)模式,屬於對象的結構模式,它將對象組織到樹結構中,可以用來描述整體與部分的關系。
模式結構
組合模式包含如下角色:
•
Component:
抽象構件
•
Leaf:
葉子構件
•
Composite:
容器構件
•
Client:
客戶類
模式分析
組合模式的關鍵是定義了一個抽象構件類,它既可以代表葉子,又可以代表容器,而客戶端針對該抽象構件類進行編程,無須知道它到底表示的是葉子還是容器,可以對其進行統一處理。
同時容器對象與抽象構件類之間還建立一個聚合關聯關系,在容器對象中既可以包含葉子,也可以包含容器,以此實現遞歸組合,形成一個樹形結構。
典型的抽象構件角色代碼:
1 public abstract class Component 2 { 3 public abstract void add(Component c); 4 public abstract void remove(Component c); 5 public abstract Component getChild(int i); 6 public abstract void operation(); 7 }
典型的葉子構件角色代碼:
1 public class Leaf extends Component 2 { 3 public void add(Component c) 4 { //異常處理或錯誤提示 } 5 6 public void remove(Component c) 7 { //異常處理或錯誤提示 } 8 9 public Component getChild(int i) 10 { //異常處理或錯誤提示 } 11 12 public void operation() 13 { 14 //實現代碼 15 } 16 }
典型的容器構件角色代碼:
1 public class Composite extends Component 2 { 3 private ArrayList list = new ArrayList(); 4 5 public void add(Component c) 6 { 7 list.add(c); 8 } 9 10 public void remove(Component c) 11 { 12 list.remove(c); 13 } 14 15 public Component getChild(int i) 16 { 17 (Component)list.get(i); 18 } 19 20 public void operation() 21 { 22 for(Object obj:list) 23 { 24 ((Component)obj).operation(); 25 } 26 } 27 }
組合模式實例與解析
實例一:水果盤
在水果盤
(Plate)中有一些水果,如蘋果(Apple)、香蕉(Banana)、梨子(Pear),當然大水果盤中還可以有小水果盤,現需要對盤中的水果進行遍歷(吃),當然如果對一個水果盤執行“吃”方法,實際上就是吃其中的水果。使用組合模式模擬該場景 。
實例代碼(JAVA):
1 //抽象構建 2 public abstract class MyElement 3 { 4 public abstract void eat(); 5 } 6 7 //容器構建 8 import java.util.*; 9 10 public class Plate extends MyElement 11 { 12 private ArrayList list=new ArrayList(); 13 14 public void add(MyElement element) 15 { 16 list.add(element); 17 } 18 19 public void delete(MyElement element) 20 { 21 list.remove(element); 22 } 23 24 public void eat() 25 { 26 for(Object object:list) 27 { 28 ((MyElement)object).eat(); //遞歸 29 } 30 } 31 } 32 33 //葉子構建 34 public class Apple extends MyElement 35 { 36 public void eat() 37 { 38 System.out.println("吃蘋果!"); 39 } 40 } 41 42 //葉子構建 43 public class Banana extends MyElement 44 { 45 public void eat() 46 { 47 System.out.println("吃香蕉!"); 48 } 49 } 50 51 //葉子構建 52 public class Pear extends MyElement 53 { 54 public void eat() 55 { 56 System.out.println("吃梨子!"); 57 } 58 } 59 60 //客戶端 61 public class Client 62 { 63 public static void main(String a[]) 64 { 65 MyElement obj1,obj2,obj3,obj4,obj5; 66 Plate plate1,plate2,plate3; 67 68 obj1=new Apple(); 69 obj2=new Pear(); 70 plate1=new Plate(); 71 plate1.add(obj1); 72 plate1.add(obj2); 73 74 obj3=new Banana(); 75 obj4=new Banana(); 76 plate2=new Plate(); 77 plate2.add(obj3); 78 plate2.add(obj4); 79 80 obj5=new Apple(); 81 plate3=new Plate(); 82 plate3.add(plate1); 83 plate3.add(plate2); 84 plate3.add(obj5); 85 86 plate3.eat(); 87 } 88 }
運行結果:
實例二:文件瀏覽
文件有不同類型,不同類型的文件其瀏覽方式有所區別,如文本文件和圖片文件的瀏覽方式就不相同。對文件夾的瀏覽實際上就是對其中所包含文件的瀏覽,而客戶端可以一致地對文件和文件夾進行操作,無須關心它們的區別。使用組合模式來模擬文件的瀏覽操作。
模式優缺點
優點
• 可以清楚地定義分層次的復雜對象,表示對象的全部或部分層次,使得增加新構件也更容易。
• 客戶端調用簡單,客戶端可以一致的使用組合結構或其中單個對象。
• 定義了包含葉子對象和容器對象的類層次結構,葉子對象可以被組合成更復雜的容器對象,而這個容器對象又可以被組合,這樣不斷遞歸下去,可以形成復雜的樹形結構。
• 更容易在組合體內加入對象構件,客戶端不必因為加入了新的對象構件而更改原有代碼。
缺點
• 使設計變得更加抽象,對象的業務規則如果很復雜,則實現組合模式具有很大挑戰性,而且不是所有的方法都與葉子對象子類都有關聯。
• 增加新構件時可能會產生一些問題,很難對容器中的構件類型進行限制。
模式適用環境
在以下情況下可以使用組合模式:
• 需要表示一個對象整體或部分層次,在具有整體和部分的層次結構中,希望通過一種方式忽略整體與部分的差異,可以一致地對待它們。
• 讓客戶能夠忽略不同對象層次的變化,客戶端可以針對抽象構件編程,無須關心對象層次結構的細節。
• 對象的結構是動態的並且復雜程度不一樣,但客戶需要一致地處理它們。
模式應用
(1) XML文檔解析
1 <?xml version="1.0"?> 2 <books> 3 <book> 4 <author>Carson</author> 5 <price format="dollar">31.95</price> 6 <pubdate>05/01/2001</pubdate> 7 </book> 8 <pubinfo> 9 <publisher>MSPress</publisher> 10 <state>WA</state> 11 </pubinfo> 12 </books>
(2)
操作系統中的目錄結構是一個樹形結構,因此在對文件和文件夾進行操作時可以應用組合模式,例如殺毒軟件在查毒或殺毒時,既可以針對一個具體文件,也可以針對一個目錄。如果是對目錄查毒或殺毒,將遞歸處理目錄中的每一個子目錄和文件。
(3)
JDK的AWT/Swing是組合模式在Java類庫中的一個典型實際應用。
模式擴展
更復雜的組合模式
透明組合模式
安全組合模式