組合模式的定義與特點
組合(Composite)模式的定義:有時又叫作部分-整體模式,它是一種将對象組合成樹狀的層次結構的模式,用來表示“部分-整體”的關系,使使用者對單個對象群組合對象具有一緻的通路性。
組合模式的主要優點有:
- 組合模式使得用戶端代碼可以一緻地處理單個對象群組合對象,無須關心自己處理的是單個對象,還是組合對象,這簡化了用戶端代碼;
- 更容易在組合體内加入新的對象,用戶端不會因為加入了新的對象而更改源代碼,滿足“開閉原則”;
其主要缺點是:
- 設計較複雜,用戶端需要花更多時間理清類之間的層次關系;
- 不容易限制容器中的構件;
- 不容易用繼承的方法來增加構件的新功能;
組合模式的結構與實作
組合模式的結構不是很複雜,下面對它的結構和實作進行分析。
1. 模式的結構
組合模式包含以下主要角色。
- 抽象構件(Component)角色:它的主要作用是為樹葉構件和樹枝構件聲明公共接口,并實作它們的預設行為。在透明式的組合模式中抽象構件還聲明通路和管理子類的接口;在安全式的組合模式中不聲明通路和管理子類的接口,管理工作由樹枝構件完成。
- 樹葉構件(Leaf)角色:是組合中的葉節點對象(葉子節點),它沒有子節點,用于實作抽象構件角色中 聲明的公共接口。
- 樹枝構件(Composite)角色:是組合中的分支節點對象(非葉子節點),它有子節點。它實作了抽象構件角色中聲明的接口,它的主要作用是存儲和管理子部件,通常包含 Add()、Remove()、GetChild() 等方法。
組合模式分為透明式的組合模式和安全式的組合模式。
(1) 透明方式:在該方式中,由于抽象構件聲明了所有子類中的全部方法,是以用戶端無須差別樹葉對象和樹枝對象,對用戶端來說是透明的。但其缺點是:樹葉構件本來沒有 Add()、Remove() 及 GetChild() 方法,卻要實作它們(空實作或抛異常),這樣會帶來一些安全性問題。其結構圖如圖 1 所示。
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiI9s2RkBnVHFmb1clWvB3MaVnRtp1XlBXe0xCMy81dvRWYoNHLwEzX5xCMx8FesU2cfdGLwMzX0xiRGZkRGZ0Xy9GbvNGLpZTY1EmMZVDUSFTU4VFRR9Fd4VGdsYTMfVmepNHLrJXYtJXZ0F2dvwVZnFWbp1zczV2YvJHctM3cv1Ce-YWan5SO0QDM2EmYycTOwcjZmNTMyYzX0ATOwUDMyIzLcJDMyIDMy8CXn9Gbi9CXzV2Zh1WavwVbvNmLvR3YxUjLyM3Lc9CX6MHc0RHaiojIsJye.gif)
代碼實作:
/**
* 組織結構,相當于Component接口
*
* @author Administrator
*/
public abstract class OrganizationComponent {
/**
* 名稱
*/
private String name;
/**
* 說明
*/
private String desc;
public OrganizationComponent(String name, String desc) {
super();
this.name = name;
this.desc = desc;
}
/**
* 新增方法,預設實作 這裡不寫成抽象方法,是考慮葉子節點不需要實作改方法
*/
protected void add(OrganizationComponent component) {
throw new UnsupportedOperationException();
}
protected void remove(OrganizationComponent component) {
throw new UnsupportedOperationException();
}
/**
* 列印方法,描述基本資訊
*/
public abstract void print();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
@Override
public String toString() {
return "OrganizationComponent [name=" + name + ", desc=" + desc + "]";
}
}
/**
* University根節點,下級為Collage,相當于Composite
*
* @author Administrator
*/
public class University extends OrganizationComponent {
/**
* list存放的是學院Collage
*/
private List<OrganizationComponent> organizationComponentList = new ArrayList<>();
public University(String name, String desc) {
super(name, desc);
}
@Override
protected void add(OrganizationComponent component) {
organizationComponentList.add(component);
}
@Override
protected void remove(OrganizationComponent component) {
organizationComponentList.remove(component);
}
@Override
public void print() {
System.out.println("================================");
System.out.println(this.toString());
// 周遊輸出學院
for (OrganizationComponent organizationComponent : organizationComponentList) {
organizationComponent.print();
}
}
@Override
public String toString() {
return "University [name=" + getName() + ", desc=" + getDesc() + "]";
}
}
/**
* 上級為University,下級為Department,相當于Composite
*
* @author Administrator
*/
public class Collage extends OrganizationComponent {
/**
* 這個list存方法的是院系Department
*/
private List<OrganizationComponent> organizationComponentList = new ArrayList<>();
public Collage(String name, String desc) {
super(name, desc);
}
@Override
protected void add(OrganizationComponent component) {
organizationComponentList.add(component);
}
@Override
protected void remove(OrganizationComponent component) {
organizationComponentList.remove(component);
}
@Override
public void print() {
System.out.println("================================");
System.out.println(this.toString());
// 周遊輸出學院
for (OrganizationComponent organizationComponent : organizationComponentList) {
organizationComponent.print();
}
}
@Override
public String toString() {
return "Collage [name=" + getName() + ", desc=" + getDesc() + "]";
}
}
/**
* Department,葉子節點,相當于Leaf
* 不需要實作add,remove方法
*
* @author Administrator
*/
public class Department extends OrganizationComponent {
public Department(String name, String desc) {
super(name, desc);
}
@Override
public void print() {
System.out.println(this.toString());
}
@Override
public String toString() {
return "Department [name=" + getName() + ", desc=" + getDesc() + "]";
}
}
public class Client {
public static void main(String[] args) {
// 建立點選節點,university
OrganizationComponent university = new University("北京大學", "中國頂級大學");
// 建立節點,collage
OrganizationComponent collage1 = new Collage("計算機學院", "中國頂級計算機學院");
OrganizationComponent collage2 = new Collage("古漢語學院", "中國頂級古漢語學院");
// 建立葉子節點,Department
OrganizationComponent department11 = new Department("軟體工程", "中國頂級軟體工程");
OrganizationComponent department12 = new Department("物聯網", "中國頂級物聯網專業");
collage1.add(department11);
collage1.add(department12);
university.add(collage1);
OrganizationComponent department21 = new Department("八股文", "中國頂級八股文專業");
collage2.add(department21);
university.add(collage2);
university.print();
}
}
運作結果:
(2) 安全方式:在該方式中,将管理子構件的方法移到樹枝構件中,抽象構件和樹葉構件沒有對子對象的管理方法,這樣就避免了上一種方式的安全性問題,但由于葉子和分支有不同的接口,用戶端在調用時要知道樹葉對象和樹枝對象的存在,是以失去了透明性。其結構圖如圖 2 所示。
2. 模式的實作
假如要通路集合 c0={leaf1,{leaf2,leaf3}} 中的元素,其對應的樹狀圖如圖 3 所示。
下面給出透明式的組合模式的實作代碼,與安全式的組合模式的實作代碼類似,隻要對其做簡單修改就可以了。
組合模式的應用場景
前面分析了組合模式的結構與特點,下面分析它适用的以下應用場景。
- 在需要表示一個對象整體與部分的層次結構的場合。
- 要求對使用者隐藏組合對象與單個對象的不同,使用者可以用統一的接口使用組合結構中的所有對象的場合。
組合模式的擴充
如果對前面介紹的組合模式中的樹葉節點和樹枝節點進行抽象,也就是說樹葉節點和樹枝節點還有子節點,這時組合模式就擴充成複雜的組合模式了,如 Java AWT/Swing 中的簡單元件 JTextComponent 有子類 JTextField、JTextArea,容器元件 Container 也有子類 Window、Panel。複雜的組合模式的結構圖如圖 5 所示。