《JAVA設計模式》之組合模式(Composite)

在閻宏博士的《JAVA與模式》一書中開頭是這樣描述合成(Composite)模式的:html

  合成模式屬於對象的結構模式,有時又叫作「部分——總體」模式。合成模式將對象組織到樹結構中,能夠用來描述總體與部分的關係。合成模式可使客戶端將單純元素與複合元素同等看待。
java


合成模式

  合成模式把部分和總體的關係用樹結構表示出來。合成模式使得客戶端把一個個單獨的成分對象和由它們複合而成的合成對象同等看待。安全

  好比,一個文件系統就是一個典型的合成模式系統。下圖是常見的計算機XP文件系統的一部分。ide

  從上圖能夠看出,文件系統是一個樹結構,樹上長有節點。樹的節點有兩種,一種是樹枝節點,即目錄,有內部樹結構,在圖中塗有顏色;另外一種是文件,即樹葉節點,沒有內部樹結構。post

  顯然,能夠把目錄和文件當作同一種對象同等對待和處理,這也就是合成模式的應用。this

  合成模式能夠不提供父對象的管理方法,可是合成模式必須在合適的地方提供子對象的管理方法,諸如:add()、remove()、以及getChild()等。url

  合成模式的實現根據所實現接口的區別分爲兩種形式,分別稱爲安全式透明式spa

安全式合成模式的結構

  安全模式的合成模式要求管理彙集的方法只出如今樹枝構件類中,而不出如今樹葉構件類中。.net

  這種形式涉及到三個角色:code

  ●  抽象構件(Component)角色:這是一個抽象角色,它給參加組合的對象定義出公共的接口及其默認行爲,能夠用來管理全部的子對象。合成對象一般把它所包含的子對象當作類型爲Component的對象。在安全式的合成模式裏,構件角色並不定義出管理子對象的方法,這必定義由樹枝構件對象給出。

  ●  樹葉構件(Leaf)角色:樹葉對象是沒有下級子對象的對象,定義出參加組合的原始對象的行爲。

  ●  樹枝構件(Composite)角色:表明參加組合的有下級子對象的對象。樹枝構件類給出全部的管理子對象的方法,如add()、remove()以及getChild()。

源代碼

 抽象構件角色類

public interface Component { /** * 輸出組建自身的名稱 */ public void printStruct(String preStr); }

 樹枝構件角色類

public class Composite implements Component { /** * 用來存儲組合對象中包含的子組件對象 */ private List<Component> childComponents = new ArrayList<Component>(); /** * 組合對象的名字 */ private String name; /** * 構造方法,傳入組合對象的名字 * @param name 組合對象的名字 */ public Composite(String name){ this.name = name; } /** * 彙集管理方法,增長一個子構件對象 * @param child 子構件對象 */ public void addChild(Component child){ childComponents.add(child); } /** * 彙集管理方法,刪除一個子構件對象 * @param index 子構件對象的下標 */ public void removeChild(int index){ childComponents.remove(index); } /** * 彙集管理方法,返回全部子構件對象 */ public List<Component> getChild(){ return childComponents; } /** * 輸出對象的自身結構 * @param preStr 前綴,主要是按照層級拼接空格,實現向後縮進 */ @Override public void printStruct(String preStr) { // 先把本身輸出 System.out.println(preStr + "+" + this.name); //若是還包含有子組件,那麼就輸出這些子組件對象 if(this.childComponents != null){ //添加兩個空格,表示向後縮進兩個空格 preStr += " "; //輸出當前對象的子對象 for(Component c : childComponents){ //遞歸輸出每一個子對象  c.printStruct(preStr); } } } }

 樹葉構件角色類

public class Leaf implements Component { /** * 葉子對象的名字 */ private String name; /** * 構造方法,傳入葉子對象的名稱 * @param name 葉子對象的名字 */ public Leaf(String name){ this.name = name; } /** * 輸出葉子對象的結構,葉子對象沒有子對象,也就是輸出葉子對象的名字 * @param preStr 前綴,主要是按照層級拼接的空格,實現向後縮進 */ @Override public void printStruct(String preStr) { // TODO Auto-generated method stub System.out.println(preStr + "-" + name); } }

 客戶端類

public class Client { public static void main(String[]args){ Composite root = new Composite("服裝"); Composite c1 = new Composite("男裝"); Composite c2 = new Composite("女裝"); Leaf leaf1 = new Leaf("襯衫"); Leaf leaf2 = new Leaf("夾克"); Leaf leaf3 = new Leaf("裙子"); Leaf leaf4 = new Leaf("套裝"); root.addChild(c1); root.addChild(c2); c1.addChild(leaf1); c1.addChild(leaf2); c2.addChild(leaf3); c2.addChild(leaf4); root.printStruct(""); } }

  能夠看出,樹枝構件類(Composite)給出了addChild()、removeChild()以及getChild()等方法的聲明和實現,而樹葉構件類則沒有給出這些方法的聲明或實現。這樣的作法是安全的作法,因爲這個特色,客戶端應用程序不可能錯誤地調用樹葉構件的彙集方法,由於樹葉構件沒有這些方法,調用會致使編譯錯誤。

  安全式合成模式的缺點是不夠透明,由於樹葉類和樹枝類將具備不一樣的接口。

透明式合成模式的結構

  與安全式的合成模式不一樣的是,透明式的合成模式要求全部的具體構件類,不論樹枝構件仍是樹葉構件,均符合一個固定接口。

  

源代碼

  抽象構件角色類

public abstract class Component { /** * 輸出組建自身的名稱 */ public abstract void printStruct(String preStr); /** * 彙集管理方法,增長一個子構件對象 * @param child 子構件對象 */ public void addChild(Component child){ /** * 缺省實現,拋出異常,由於葉子對象沒有此功能 * 或者子組件沒有實現這個功能 */ throw new UnsupportedOperationException("對象不支持此功能"); } /** * 彙集管理方法,刪除一個子構件對象 * @param index 子構件對象的下標 */ public void removeChild(int index){ /** * 缺省實現,拋出異常,由於葉子對象沒有此功能 * 或者子組件沒有實現這個功能 */ throw new UnsupportedOperationException("對象不支持此功能"); } /** * 彙集管理方法,返回全部子構件對象 */ public List<Component> getChild(){ /** * 缺省實現,拋出異常,由於葉子對象沒有此功能 * 或者子組件沒有實現這個功能 */ throw new UnsupportedOperationException("對象不支持此功能"); } }

  樹枝構件角色類,此類將implements Conponent改成extends Conponent,其餘地方無變化。

public class Composite extends Component { /** * 用來存儲組合對象中包含的子組件對象 */ private List<Component> childComponents = new ArrayList<Component>(); /** * 組合對象的名字 */ private String name; /** * 構造方法,傳入組合對象的名字 * @param name 組合對象的名字 */ public Composite(String name){ this.name = name; } /** * 彙集管理方法,增長一個子構件對象 * @param child 子構件對象 */ public void addChild(Component child){ childComponents.add(child); } /** * 彙集管理方法,刪除一個子構件對象 * @param index 子構件對象的下標 */ public void removeChild(int index){ childComponents.remove(index); } /** * 彙集管理方法,返回全部子構件對象 */ public List<Component> getChild(){ return childComponents; } /** * 輸出對象的自身結構 * @param preStr 前綴,主要是按照層級拼接空格,實現向後縮進 */ @Override public void printStruct(String preStr) { // 先把本身輸出 System.out.println(preStr + "+" + this.name); //若是還包含有子組件,那麼就輸出這些子組件對象 if(this.childComponents != null){ //添加兩個空格,表示向後縮進兩個空格 preStr += " "; //輸出當前對象的子對象 for(Component c : childComponents){ //遞歸輸出每一個子對象  c.printStruct(preStr); } } } }

  樹葉構件角色類,此類將implements Conponent改成extends Conponent,其餘地方無變化。

public class Leaf extends Component { /** * 葉子對象的名字 */ private String name; /** * 構造方法,傳入葉子對象的名稱 * @param name 葉子對象的名字 */ public Leaf(String name){ this.name = name; } /** * 輸出葉子對象的結構,葉子對象沒有子對象,也就是輸出葉子對象的名字 * @param preStr 前綴,主要是按照層級拼接的空格,實現向後縮進 */ @Override public void printStruct(String preStr) { // TODO Auto-generated method stub System.out.println(preStr + "-" + name); } }

  客戶端類的主要變化是再也不區分Composite對象和Leaf對象。

public class Client { public static void main(String[]args){ Component root = new Composite("服裝"); Component c1 = new Composite("男裝"); Component c2 = new Composite("女裝"); Component leaf1 = new Leaf("襯衫"); Component leaf2 = new Leaf("夾克"); Component leaf3 = new Leaf("裙子"); Component leaf4 = new Leaf("套裝"); root.addChild(c1); root.addChild(c2); c1.addChild(leaf1); c1.addChild(leaf2); c2.addChild(leaf3); c2.addChild(leaf4); root.printStruct(""); } }

  能夠看出,客戶端無需再區分操做的是樹枝對象(Composite)仍是樹葉對象(Leaf)了;對於客戶端而言,操做的都是Component對象。

兩種實現方法的選擇

  這裏所說的安全性合成模式是指:從客戶端使用合成模式上看是否更安全,若是是安全的,那麼就不會有發生誤操做的可能,能訪問的方法都是被支持的。

  這裏所說的透明性合成模式是指:從客戶端使用合成模式上,是否須要區分究竟是「樹枝對象」仍是「樹葉對象」。若是是透明的,那就不用區分,對於客戶而言,都是Compoent對象,具體的類型對於客戶端而言是透明的,是無須關心的。

  對於合成模式而言,在安全性和透明性上,會更看重透明性,畢竟合成模式的目的是:讓客戶端再也不區分操做的是樹枝對象仍是樹葉對象,而是以一個統一的方式來操做。

  並且對於安全性的實現,須要區分是樹枝對象仍是樹葉對象。有時候,須要將對象進行類型轉換,卻發現類型信息丟失了,只好強行轉換,這種類型轉換必然是不夠安全的。

  所以在使用合成模式的時候,建議多采用透明性的實現方式。 

 

原文地址:《JAVA與模式》之合成模式

相關文章
相關標籤/搜索