標籤:外部 用戶端 site component 管理 something etc 索引 分享圖片
Java 設計模式系列(九)組合模式
將對象組合成樹形結構以表示“部分-整體”的階層。組合模式使得使用者對單個對象的使用具有一致性。
一、組合模式結構
-
Component: 抽象的組件對象,為組合中的對象聲明介面,讓用戶端可以通過這個介面來訪問和管理整個對象結構,可以在裡面為定義的功能提供預設的實現。
-
Leaf: 葉子節點對象,定義和實現葉子物件的行為,不再包含其它的子節點對象。
-
Composite: 組合對象,通常會儲存子組件,定義包含子組件的那些組件的行為,並實現在組件介面中定義的與子組件有關的操作。
-
Client: 用戶端,通過組件介面來操作組合結構裡面的組件對象。
組合模式參考實現
(1)先看看組件對象的定義,範例程式碼如下:
/** * 抽象的組件對象,為組合中的對象聲明介面,實現介面的預設行為 */public abstract class Component { /** 示意方法,子組件對象可能有的功能方法 */ public abstract void someOperation(); /** 向組合對象中加入組件對象 */ public void addChild(Component child) { // 預設的實現,拋出例外,因為葉子物件沒有這個功能,或者子組件沒有實現這個功能 throw new UnsupportedOperationException("對象不支援這個功能"); } /** 從組合對象中移出某個組件對象 */ public void removeChild(Component child) { // 預設的實現,拋出例外,因為葉子物件沒有這個功能,或者子組件沒有實現這個功能 throw new UnsupportedOperationException("對象不支援這個功能"); } /** 返回某個索引對應的組件對象 */ public Component getChildren(int index) { // 預設的實現,拋出例外,因為葉子物件沒有這個功能,或者子組件沒有實現這個功能 throw new UnsupportedOperationException("對象不支援這個功能"); }}
(2)接下來看看Composite對象的定義,範例程式碼如下:
/** * 組合對象,通常需要儲存子物件,定義有子組件的組件行為, * 並實現在Component裡面定義的與子組件有關的操作 */public class Composite extends Component { /** * 用來儲存群組合對象中包含子群組件對象 */ private List<Component> childComponents = null; /** 示意方法,通常在裡面需要實現遞迴的調用 */ public void someOperation() { if (childComponents != null){ for(Component c : childComponents){ //遞迴的進行子組件相應方法的調用 c.someOperation(); } } } public void addChild(Component child) { //延遲初始化 if (childComponents == null) { childComponents = new ArrayList<Component>(); } childComponents.add(child); } public void removeChild(Component child) { if (childComponents != null) { childComponents.remove(child); } } public Component getChildren(int index) { if (childComponents != null){ if(index>=0 && index<childComponents.size()){ return childComponents.get(index); } } return null; }}
(3)該來看葉子物件的定義了,相對而言比較簡單,範例程式碼如下:
/** * 葉子物件,葉子物件不再包含其它子物件 */public class Leaf extends Component { /** 示意方法,葉子物件可能有自己的功能方法 */ public void someOperation() { // do something }}
(4)對於Client,就是使用Component介面來操作組合對象結構,由於使用方式千差萬別,這裡僅僅提供一個示範性質的使用,順便當作測試代碼使用,範例程式碼如下:
public class Client { public static void main(String[] args) { //定義多個Composite對象 Component root = new Composite(); Component c1 = new Composite(); Component c2 = new Composite(); //定義多個葉子物件 Component leaf1 = new Leaf(); Component leaf2 = new Leaf(); Component leaf3 = new Leaf(); //組和成為樹形的對象結構 root.addChild(c1); root.addChild(c2); root.addChild(leaf1); c1.addChild(leaf2); c2.addChild(leaf3); //操作Component對象 Component o = root.getChildren(1); System.out.println(o); }}
二、父組件引用
(1) Component
public abstract class Component { /** * 記錄父組件對象 */ private Component parent = null; /** * 擷取一個組件的父組件對象 * @return 一個組件的父組件對象 */ public Component getParent() { return parent; } /** * 設定一個組件的父組件對象 * @param parent 一個組件的父組件對象 */ public void setParent(Component parent) { this.parent = parent; } /** * 返回某個組件的子組件對象 * @return 某個組件的子組件對象 */ public List<Component> getChildren() { throw new UnsupportedOperationException("對象不支援這個功能"); } /*-------------------以下是原有的定義----------------------*/ /** * 輸出組件自身的名稱 */ public abstract void printStruct(String preStr); /** * 向組合對象中加入組件對象 * @param child 被加入組合對象中的組件對象 */ public void addChild(Component child) { // 預設的實現,拋出例外,因為葉子物件沒有這個功能,或者子組件沒有實現這個功能 throw new UnsupportedOperationException("對象不支援這個功能"); } /** * 從組合對象中移出某個組件對象 * @param child 被移出的組件對象 */ public void removeChild(Component child) { // 預設的實現,拋出例外,因為葉子物件沒有這個功能,或者子組件沒有實現這個功能 throw new UnsupportedOperationException("對象不支援這個功能"); } /** * 返回某個索引對應的組件對象 * @param index 需要擷取的組件對象的索引,索引從0開始 * @return 索引對應的組件對象 */ public Component getChildren(int index) { throw new UnsupportedOperationException("對象不支援這個功能"); }}
(2) Leaf
public class Leaf extends Component { /** * 葉子物件的名字 */ private String name = ""; /** * 構造方法,傳入葉子物件的名字 * @param name 葉子物件的名字 */ public Leaf(String name){ this.name = name; } /** * 輸出葉子物件的結構,葉子物件沒有子物件,也就是輸出葉子物件的名字 * @param preStr 首碼,主要是按照層級拼接的空格,實現向後縮排 */ public void printStruct(String preStr){ System.out.println(preStr+"-"+name); }}
(3) Composite
public class Composite extends Component{ public void addChild(Component child) { //延遲初始化 if (childComponents == null) { childComponents = new ArrayList<Component>(); } childComponents.add(child); //添加對父組件的引用 child.setParent(this); } public void removeChild(Component child) { if (childComponents != null) { //尋找到要刪除的組件在集合中的索引位置 int idx = childComponents.indexOf(child); if (idx != -1) { //先把被刪除的商品類別對象的父商品類別,設定成為被刪除的商品類別的子類別的父商品類別 for(Component c : child.getChildren()){ //刪除的組件對象是本執行個體的一個子組件對象 c.setParent(this); //把被刪除的商品類別對象的子組件對象添加到當前執行個體中 childComponents.add(c); } //真的刪除 childComponents.remove(idx); } } } public List<Component> getChildren() { return childComponents; } /*-------------------以下是原有的實現,沒有變化----------------------*/ /** * 用來儲存群組合對象中包含子群組件對象 */ private List<Component> childComponents = null; /** * 組合對象的名字 */ private String name = ""; /** * 構造方法,傳入組合對象的名字 * @param name 組合對象的名字 */ public Composite(String name){ this.name = name; } /** * 輸出組合對象自身的結構 * @param preStr 首碼,主要是按照層級拼接的空格,實現向後縮排 */ public void printStruct(String preStr){ //先把自己輸出去 System.out.println(preStr+"+"+this.name); //如果還包含有子組件,那麼就輸出這些子組件對象 if(this.childComponents!=null){ //然後添加一個空格,表示向後縮排一個空格 preStr+=" "; //輸出當前對象的子物件了 for(Component c : childComponents){ //遞迴輸出每個子物件 c.printStruct(preStr); } } }}
三、總結(1) 觀察者模式的本質
組合模式的本質: 統一葉子物件和組合對象。
組合模式通過把葉子物件當成特殊的組合對象看待,從而對葉子物件和組合對象一視同仁,統統當成了Component對象,有機的統一了葉子物件和組合對象。
正是因為統一了葉子物件和組合對象,在將對象構建成樹形結構的時候,才不需要做區分,反正是組件對象裡麵包含其它的組件對象,如此遞迴下去;也才使得對於樹形結構的操作變得簡單,不管物件類型,統一操作。
(2) 何時選用觀察者模式
(3) 觀察者模式的優缺點
-
定義了包含基本對象和組合對象的類階層
在組合模式中,基本對象可以被組合成更複雜的組合對象,而組合對象又可以組合成更複雜的組合對象,可以不斷地遞迴組合下去,從而構成一個統一的組合對象的類階層
-
統一了組合對象和葉子物件
在組合模式中,可以把葉子物件當作特殊的組合對象看待,為它們定義統一的父類,從而把組合對象和葉子物件的行為統一起來
-
簡化了用戶端調用
組合模式通過統一組合對象和葉子物件,使得用戶端在使用它們的時候,就不需要再去區分它們,客戶不關心使用的到底是什麼類型的對象,這就大大簡化了用戶端的使用
-
更容易擴充
由於用戶端是統一的面對Component來操作,因此,新定義的Composite或Leaf子類能夠很容易的與已有的結構一起工作,而用戶端不需要為增添了新的組件類而改變
-
很難限制組合中的組件類型
容易增加新的組件也會帶來一些問題,比如很難限制組合中的組件類型。這在需要檢測組件類型的時候,使得我們不能依靠編譯期的類型約束來完成,必須在運行期間動態檢測。
每天用心記錄一點點。內容也許不重要,但習慣很重要!
Java 設計模式系列(九)組合模式