1.情境類比
使用軟體類比大樹的根節點和樹枝節點和葉子節點
抽象為兩類,容器節點和葉子節點
2.不用模式的解決方案
package demo14.composite.example1;import java.util.*;/** * 組合對象,可以包含其它組合對象或者葉子物件 */public class Composite {/** * 用來記錄包含的其它組合對象 */private Collection<Composite> childComposite = new ArrayList<Composite>();/** * 用來記錄包含的其它葉子物件 */private Collection<Leaf> childLeaf = new ArrayList<Leaf>();/** * 組合對象的名字 */private String name = "";/** * 構造方法,傳入組合對象的名字 * @param name 組合對象的名字 */public Composite(String name){this.name = name;}/** * 向組合對象加入被它包含的其它組合對象 * @param c 被它包含的其它組合對象 */public void addComposite(Composite c){this.childComposite.add(c);}/** * 向組合對象加入被它包含的葉子物件 * @param leaf 被它包含的葉子物件 */public void addLeaf(Leaf leaf){this.childLeaf.add(leaf);}/** * 輸出組合對象自身的結構 * @param preStr 首碼,主要是按照層級拼接的空格,實現向後縮排 */public void printStruct(String preStr){//先把自己輸出去System.out.println(preStr+"+"+this.name);//然後添加一個空格,表示向後縮排一個空格,輸出自己包含的葉子物件preStr+=" ";for(Leaf leaf : childLeaf){leaf.printStruct(preStr);}//輸出當前對象的子物件了for(Composite c : childComposite){//遞迴輸出每個子物件c.printStruct(preStr);}}}***********************************************************************************************package demo14.composite.example1;/** * 葉子物件 */public class Leaf {/** * 葉子物件的名字 */private String name = "";/** * 構造方法,傳入葉子物件的名字 * @param name 葉子物件的名字 */public Leaf(String name){this.name = name;}/** * 輸出葉子物件的結構,葉子物件沒有子物件,也就是輸出葉子物件的名字 * @param preStr 首碼,主要是按照層級拼接的空格,實現向後縮排 */public void printStruct(String preStr){System.out.println(preStr+"-"+name);}}***************************************************************************************************package demo14.composite.example1;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.addComposite(c1);root.addComposite(c2);c1.addLeaf(leaf1);c1.addLeaf(leaf2);c2.addLeaf(leaf3);c2.addLeaf(leaf4);// 調用根對象的輸出功能來輸出整棵樹root.printStruct("");}}3.問題所在
大家很容易就發現了一個問題:那就是必須區分葉子節點和容器節點,無論是用戶端還是內部都要區分,那麼這樣就會使得程式變得複雜,對於功能的擴充也不方便,而且要知道兩個類才行,那麼我們能不能把他整合成一個類呢,讓用戶端如下顯示呢?答案是可以的。
package demo14.composite.example2;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);}}4.解決方案的範例程式碼
很明顯,上述用戶端的代碼就順眼多了,那麼我們改如何?呢?
4.1首先看結構圖
4.2組合模式定義
將對象組合成樹形結構以表示“部分-整體”的階層。組合模式使得使用者對單個對象和組合對象的使用具有一致性。
4.3範例程式碼如下
package demo14.composite.example2;/** * 抽象的組件對象,為組合中的對象聲明介面,實現介面的預設行為 */public abstract class Component {/** * 示意方法,子組件對象可能有的功能方法 */public abstract void someOperation();/** * 向組合對象中加入組件對象 * * @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("對象不支援這個功能");}}**********************************************************************package demo14.composite.example2;import java.util.*;/** * 組合對象,通常需要儲存子物件,定義有子組件的組件行為, * 並實現在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;}}**********************************************************************package demo14.composite.example2;/** * 葉子物件,葉子物件不再包含其它子物件 */public class Leaf extends Component {/** * 示意方法,葉子物件可能有自己的功能方法 */public void someOperation() {// do something}}**********************************************************************package demo14.composite.example2;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);}}5.組合模式解決情境類比
代碼的修改量並不大,跟範例程式碼差不多
5.1結構圖如下
5.2代碼如下(基本上同範例程式碼差不多,就不過多介紹了,注釋非常清楚)
package demo14.composite.example3;/** * 抽象的組件對象 */public abstract class Component {/** * 輸出組件自身的名稱 */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("對象不支援這個功能");}}*********************************************************************************************package demo14.composite.example3;import java.util.*;/** * 組合對象,可以包含其它組合對象或者葉子物件 */public class Composite extends Component{/** * 用來儲存群組合對象中包含子群組件對象 */private List<Component> childComponents = null;/** * 組合對象的名字 */private String name = "";/** * 構造方法,傳入組合對象的名字 * @param name 組合對象的名字 */public Composite(String name){this.name = name;}public void addChild(Component child) {//延遲初始化if (childComponents == null) {childComponents = new ArrayList<Component>();}childComponents.add(child);}/** * 輸出組合對象自身的結構 * @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);}}}}**********************************************************************************************package demo14.composite.example3;/** * 葉子物件 */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);}}********************************************************************************************package demo14.composite.example3;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("");}}6.組合模式講解6.1要點
目的:用戶端不再區分葉子節點和組合對象
關鍵點:設計一個抽象的組合對象
6.2組合模式本質
統一葉子物件和組合對象