一、概念 將對象組合成樹形結構以表示“部分-整體”的層次結構。組合模式使得用戶對單個對象和組合對象的使用具有一致性。 二、模式動機 組合模式,通過設計一個抽像的組件類,使它既代表葉子對象,又代表組合對象,將葉子對象和組合對象統一起來。使得客戶端在操作時不再區分當前操作的是葉子對象還是組合對象,而是以 ...
一、概念
將對象組合成樹形結構以表示“部分-整體”的層次結構。組合模式使得用戶對單個對象和組合對象的使用具有一致性。
二、模式動機
組合模式,通過設計一個抽像的組件類,使它既代表葉子對象,又代表組合對象,將葉子對象和組合對象統一起來。使得客戶端在操作時不再區分當前操作的是葉子對象還是組合對象,而是以一個統一方式來操作。
三、模式的結構
示意代碼如下:
package composite.structure; /** * 統一葉子和組件的抽象構件類 * @ClassName: Component * @author beteman6988 * @date 2017年11月29日 下午10:48:18 * */ public abstract class Component { /** * 葉子和樹技構件都具備的商業邏輯 * @Title: sampleOperation * @param * @return void * @throws */ public abstract void sampleOperation(); /** * 向組合對象中添加組件對象 * @Title: addChild * @param @param child * @return void * @throws */ public abstract void addChild(Component child); /** * 向組合對象中刪除組件對象 * @param child * @roseuid 5A1EC291037D */ public abstract void removeChild(Component child) ; /** * 從組合對象中取出第index個位置的組件對象 * @param index * @return Component * @roseuid 5A1EC2F2011B */ public abstract Component getChildren(Integer index) ; }
package composite.structure; import java.util.ArrayList; import java.util.List; /** * 組合對像,通常存儲子組件對象,實現抽象組件裡面定義的與操作子組件對象有關的行為。 * @ClassName: Composite * @author beteman6988 * @date 2017年12月3日 上午9:22:08 * */ public class Composite extends Component { List<Component> childComponent=new ArrayList<Component>(); /** * 商業邏輯,這裡樣例迭代所有子組件的該方法 */ @Override public void sampleOperation() { for(Component component:childComponent) { component.sampleOperation(); } } @Override public void addChild(Component child) { this.childComponent.add(child); } @Override public void removeChild(Component child) { this.childComponent.remove(child); } @Override public Component getChildren(Integer index) { if(!childComponent.isEmpty() && childComponent.size()>index ) { return this.childComponent.get(index); } return null; } }
package composite.structure; /** * 葉子組件對象 * @ClassName: Leaf * @author beteman6988 * @date 2017年12月3日 上午9:25:42 * */ public class Leaf extends Component { /** * 商業邏輯 */ @Override public void sampleOperation() { //具體的商業邏輯 } /** * 由於是葉子對象節點,不具備增加組件對象的功能,所以可以提供平庸實現 */ @Override public void addChild(Component child) { } /** * 由於是葉子對象節點,不具備刪除組件對象的功能,所以可以提供平庸實現 */ @Override public void removeChild(Component child) { // TODO Auto-generated method stub } /** * 由於是葉子對象節點,不具獲取子組件對象的功能,所以可以提供平庸實現 */ @Override public Component getChildren(Integer index) { // TODO Auto-generated method stub return null; } }
角色說明:
Component:抽象的組件對象,可以是一個抽象類或者是一個介面,它既代表葉子對象,也代組合對象,讓客戶端通過這個介面來管理整個對象結構,上面的樣例代碼定義的是一個抽象類,且管理組合對象的方法是抽象的,其實也可以提供預設的實現。不提供預設實現的好處就是葉子對象針對這些不支持的行為必須給出明確的處理方式。提供預設形為的好處,如果預設的是針對葉子對象的行為,那麼針對葉子對象就不需要對這些不支持的形為提供明確的處理方式。並沒有說哪種就好哪種就劣,各有優缺。
Composite:組合對象,通常存儲子組件,並包含操作子組件的具體行為,如對子組件進行增刪改查。
Leaf:葉子節點組件:定義和實現與葉子對象的行為,不能包含其它子組件。
在組合模式中,對象分為兩種,一為Composite對象,它可以包含其它的Composite或葉子對象,就像一個容器,二為不能包含子組件的Leaf對象。對於Composite對像,為了管理子組件對象,就要定以一些管理子組件的操作行為,以上的例 子中將這些操作形為定義在了抽象的Component組件中,但是這些行為其實對於Leaf對象是沒有意義的,這些行為可以放在Composite中,而非放在在Component中。所以跟據Composite對象中管理子組件的行為操作是放在Componnet或是Composite中,細分為了“透明模式”和“安全模式”
透明模式:上面的示例結構為透明模式,透明模式對於客戶端來說,Leaf對象和Composite對象具有相同的行為,對於客戶端而言,Leaf對角和Composite對象是透明的,他們都是Component對象,具有相同的介面行為,也可以在Leaf對象上使用增加子組件的行為,只是這些行為對於Leaf對象是無意義的,這也意為著是不安全的。
安全模式:針對透明模式中,管理子組件的行為如果不定義在Component中,而是定義在Composite中,這樣對於Leaf對象就是安全的了。因為客戶端對於Leaf對象,就無在調用管理子組件的行為。模式結構如下:
示意代碼如下:
package composite.structure.safety; /** * 統一葉子和組件的抽象構件類 * @ClassName: Component * @author beteman6988 * @date 2017年11月29日 下午10:48:18 * */ public abstract class Component { /** * 葉子和樹技構件都具備的商業邏輯 * @Title: sampleOperation * @param * @return void * @throws */ public abstract void sampleOperation(); }
package composite.structure.safety; import java.util.ArrayList; import java.util.List; /** * 組合對像,通常存儲子組件對象,實現抽象組件裡面定義的與操作子組件對象有關的行為。 * @ClassName: Composite * @author beteman6988 * @date 2017年12月3日 上午9:22:08 * */ public class Composite extends Component { List<Component> childComponent=new ArrayList<Component>(); /** * 商業邏輯,這裡樣例迭代所有子組件的該方法 */ @Override public void sampleOperation() { for(Component component:childComponent) { component.sampleOperation(); } } /** * 向組合對象中添加組件對象 * @Title: addChild * @param @param child * @return void * @throws */ public void addChild(Component child) { this.childComponent.add(child); } /** * 向組合對象中刪除組件對象 * @Title: removeChild * @param @param child * @return void * @throws */ public void removeChild(Component child) { this.childComponent.remove(child); } /** * 從組合對象中取出第index個位置的組件對象 * @Title: getChildren * @param @param index * @param @return * @return Component * @throws */ public Component getChildren(Integer index) { if(!childComponent.isEmpty() && childComponent.size()>index ) { return this.childComponent.get(index); } return null; } }
package composite.structure.safety; /** * 葉子組件對象 * @ClassName: Leaf * @author beteman6988 * @date 2017年12月3日 上午9:25:42 * */ public class Leaf extends Component { /** * 商業邏輯 */ @Override public void sampleOperation() { //具體的商業邏輯 } }
四、模式樣例
列印出以常見學的學校組織架構為例,如下:
XX大學
信息工程學院
電腦科學系
電子與信息工程系
建築工程學院
土木建築系
工程管理系
透明模式:
package composite.sample; /** * 院系抽像介面 * @ClassName: SCCompent * @author beteman6988 * @date 2017年12月9日 下午8:15:46 * */ public interface SCComponent { /** * get院系名稱 * @Title: getName * @param @return * @return String * @throws */ public String getName(); /** * 設置院系名稱 * @Title: setName * @param @return * @return * @throws */ public void setName(String name); /** * 添加下級院系 * @Title: addSCc * @param @param scComponent * @return void * @throws */ public void addSCc(SCComponent scComponent); /** * 移去下級院系 * @Title: removeSCc * @param @param scComponent * @return void * @throws */ public void removeSCc(SCComponent scComponent); /** * 列印院系名稱 * @Title: printName * @param @param preStr * @return void * @throws */ public void printName(String preStr); }
package composite.sample; /** * 葉子系節點 * @ClassName: Department * @author beteman6988 * @date 2017年12月9日 下午8:25:53 * */ public class Department implements SCComponent { private String departmentName; @Override public String getName() { // TODO Auto-generated method stub return departmentName; } @Override public void setName(String name) { this.departmentName=name; } @Override public void addSCc(SCComponent scComponent) { System.out.println("Department不支持addSCc行為!"); } @Override public void removeSCc(SCComponent scComponent) { System.out.println("Department不支持removeSCc行為!"); } @Override public void printName(String preStr) { System.out.println(preStr+this.departmentName); } }
package composite.sample; import java.util.ArrayList; import java.util.List; /** *院系設置的組合對象 * @ClassName: SCComposite * @author beteman6988 * @date 2017年12月9日 下午8:37:44 * */ public class SCComposite implements SCComponent { private String name; private List<SCComponent> sCCList=new ArrayList<SCComponent>(); @Override public String getName() { return this.name; } @Override public void setName(String name) { this.name=name; } @Override public void addSCc(SCComponent scComponent) { if(sCCList.indexOf(scComponent)<0) { sCCList.add(scComponent); } } @Override public void removeSCc(SCComponent scComponent) { this.sCCList.remove(scComponent); } @Override public void printName(String preStr) { System.out.println(preStr+this.name); for(SCComponent component:sCCList) { component.printName(preStr+" "); } } }
package composite.sample; public class Client { public static void main(String[] args) { SCComponent head=new SCComposite(); head.setName("XX大學"); SCComponent sCCOne=new SCComposite(); sCCOne.setName("信息工程學院"); SCComponent sCCTwo=new SCComposite(); sCCTwo.setName("建築工程學院"); head.addSCc(sCCOne); head.addSCc(sCCTwo); SCComponent dptOne=new Department(); dptOne.setName("電腦科學系"); SCComponent dptTwo=new Department(); dptTwo.setName("電子與信息工程系"); sCCOne.addSCc(dptOne); sCCOne.addSCc(dptTwo); SCComponent dptThree=new Department(); dptThree.setName("土木建築系"); SCComponent dptFour=new Department(); dptFour.setName("工程管理系"); sCCTwo.addSCc(dptThree); sCCTwo.addSCc(dptFour); //sCCTwo.removeSCc(dptThree); head.printName(""); } }
運行結果如下:
XX大學
信息工程學院
電腦科學系
電子與信息工程系
建築工程學院
工程管理系
安全模式:
代碼如下:
package composite.sample.safety; /** * 院系抽像介面 * @ClassName: SCCompent * @author beteman6988 * @date 2017年12月9日 下午8:15:46 * */ public interface SCComponent { /** * get院系名稱 * @Title: getName * @param @return * @return String * @throws */ public String getName(); /** * 設置院系名稱 * @Title: setName * @param @return * @return * @throws */ public void setName(String name); /** * 列印院系名稱 * @Title: printName * @param @param preStr * @return void * @throws */ public void printName(String preStr); }
package composite.sample.safety; /** * 葉子系節點 * @ClassName: Department * @author beteman6988 * @date 2017年12月9日 下午8:25:53 * */ public class Department implements SCComponent { private String departmentName; @Override public String getName() { // TODO Auto-generated method stub return departmentName; } @Override public void setName(String name) { this.departmentName=name; } @Override public void printName(String preStr) { System.out.println(preStr+this.departmentName); } }
package composite.sample.safety; import java.util.ArrayList; import java.util.List; /** *院系設置的組合對象 * @ClassName: SCComposite * @author beteman6988 * @date 2017年12月9日 下午8:37:44 * */ public class SCComposite implements SCComponent { private String name; private List<SCComponent> sCCList=new ArrayList<SCComponent>(); @Override public String getName() { return this.name; } @Override public void setName(String name) { this.name=name; } public void addSCc(SCComponent scComponent) { if(sCCList.indexOf(scComponent)<0) { sCCList.add(scComponent); } } public void removeSCc(SCComponent scComponent) { this.sCCList.remove(scComponent); } @Override public void printName(String preStr) { System.out.println(preStr+this.name); for(SCComponent component:sCCList) { component.printName(preStr+" "); } } }
package composite.sample.safety; public class Client { public static void main(String[] args) { SCComposite head=new SCComposite(); head.setName("XX大學"); SCComposite sCCOne=new SCComposite(); sCCOne.setName("信息工程學院"); SCComposite sCCTwo=new SCComposite(); sCCTwo.setName("建築工程學院"); head.addSCc(sCCOne); head.addSCc(sCCTwo); SCComponent dptOne=new Department(); dptOne.setName("電腦科學系"); SCComponent dptTwo=new Department(); dptTwo.setName("電子與信息工程系"); sCCOne.addSCc(dptOne); sCCOne.addSCc(dptTwo); SCComponent dptThree=new Department(); dptThree.setName("土木建築系"); SCComponent dptFour=new Department(); dptFour.setName("工程管理系"); sCCTwo.addSCc(dptThree); sCCTwo.addSCc(dptFour); //sCCTwo.removeSCc(dptThree); head.printName(""); } }
運行結果如下:
XX大學
信息工程學院
電腦科學系
電子與信息工程系
建築工程學院
土木建築系
工程管理系
五、模式優缺點
優點:1.定義了包含基本對象和組合對象的類層次結構
2.統一了組合對象和葉子對象,在組合模式中,可以把葉子對象當成特殊的組合對象來看待,為他們定義統一的父類,從而把葉子對象和組合對象的行為統一起來。
3.方便擴展,只要是Component的子類對象,都可以很方便添加到組合對象中。
缺點:優點中的第3點既是優點,也是缺點,組合對象很難確定裡面的具體組件類型,因為只要是Component的子類對象,都可以添加到組合對象中,所以當要確定組合對象中組件具體的類型時,就必須在運行期動態檢測。