
一、簡述
組合模式(Composite Pattern),也稱作部分整體模式(Part-Whole Pattern),將一組相似的對象看做一個對象處理,并根據(jù)一個樹狀結構來組合對象;對象都提供一個統(tǒng)一的方法去訪問相應的對象來處理多個對象的同一性問題。
組合模式屬于結構設計模式之一,而其設計目的就是將對象組合成樹形結構以表示"部分-整體"的層次結構,使得用戶對單個對象和組合對象的使用具有一致性;所以決定組合模式的設計基礎就是樹狀結構,組合模式所適用的情況也就是樹狀結構或者適合適用樹狀結構來解決問題的情況。

- Component:抽象節(jié)點,為組合中的對象聲明統(tǒng)一接口
- Composite:可以儲存子節(jié)點的節(jié)點對象,并實現(xiàn)抽象節(jié)點的有關操作
- Leaf:葉子節(jié)點,沒有子節(jié)點對象
- Client:組合節(jié)點對象,進行操作
這里所描述的是透明的組合模式,可以看到Component類中除了統(tǒng)一的操作方法doSomthing()方法以外,還有操作子節(jié)點的相關方法,而葉子節(jié)點Leaf類定義就是沒有葉子節(jié)點的,顯然這些操作子節(jié)點的方法就是多余的。
如果要讓Leaf類不繼承這些方法,只能將Component類中的這些方法放到它的子類Composite中;然而這樣的設計方式與依賴倒置原則相違背,所以這里并沒有采用這種組合模式,即安全的組合模式。
二、實現(xiàn)
說到組合模式,最適合的就是文件系統(tǒng)的結構了,文件夾中就子文件夾和文件,子文件夾中可能又是如此,典型的樹狀結構。
抽象的目錄類,有目錄名,有輸出目錄名,并提供添加目錄、刪除目錄以及清空目錄的的功能方法
public abstract class Directory {
//當前目錄名
private final String name;
public Directory(String name) {
this.name = name;
}
/**
* 輸出目錄結構
*/
public abstract void print();
/**
* 添加一個文件或者文件夾
* @param dir
*/
public abstract void addDir(Directory dir);
/**
* 刪除一個文件或者文件夾
* @param dir
*/
public abstract void removeDir(Directory dir);
/**
* 清空目錄
*/
public abstract void clear();
/**
* 獲取目錄中的所有目錄
* @return
*/
public abstract List<Directory> getDirectories();
/**
* 獲取目錄名
* @return
*/
public String getName() {
return name;
}
}
文件夾類,申明一個集合儲存自身所報的目錄,實現(xiàn)了具體的目錄操作方法,在print()方法中循環(huán)調(diào)用集合中目錄的print()方法輸出
public class Folder extends Directory {
/**
* 當前文件夾下的所有目錄元素
*/
protected List<Directory> directories = new ArrayList<>();
public Folder(String name) {
super(name);
}
@Override
public void print() {
System.out.print(getName() + "[");
Iterator<Directory> iterator = directories.iterator();
while (iterator.hasNext()){
Directory directory = iterator.next();
directory.print();
if(iterator.hasNext()){
System.out.print(", ");
}
}
System.out.print("]");
}
@Override
public void addDir(Directory dir) {
directories.add(dir);
}
@Override
public void removeDir(Directory dir) {
directories.remove(dir);
}
@Override
public void clear() {
directories.clear();
}
@Override
public List<Directory> getDirectories() {
return directories;
}
}
文件類,實現(xiàn)了print()方法,由于沒有子目錄,相關操作的方法都拋出異常
public class File extends Directory {
public File(String name) {
super(name);
}
@Override
public void print() {
System.out.print(getName());
}
@Override
public void addDir(Directory dir) {
throw new UnsupportedOperationException("文件對象不支持該操作");
}
@Override
public void removeDir(Directory dir) {
throw new UnsupportedOperationException("文件對象不支持該操作");
}
@Override
public void clear() {
throw new UnsupportedOperationException("文件對象不支持該操作");
}
@Override
public List<Directory> getDirectories() {
throw new UnsupportedOperationException("文件對象不支持該操作");
}
}
測試代碼,模擬輸出C盤的結構
public class Client {
public static void main(String[] args){
Directory root = new Folder("C");
root.addDir(new Folder("windows"));
Directory program = new Folder("Program File(x86)");
program.addDir(new Folder("Intellij"));
program.addDir(new File("cache"));
root.addDir(program);
root.addDir(new Folder("windows"));
root.addDir(new File("log.txt"));
root.addDir(new File("null.txt"));
root.print();
}
}
輸出結果:
C[windows[], Program File(x86)[Intellij[], cache], windows[], log.txt, null.txt]
三、在Android中的實現(xiàn)
組合模式在Android的View和ViewGroup的嵌套組合使用中得到了很好地展現(xiàn)。采用的事安全的組合模式

省略了View和ViewGroup的很多方法,在Android中,只有ViewGroup才能放View(ViewGroup也是View),View并不是容器所以ViewGroup相對于View多了幾個操作視圖的方法。
看看ViewGroup的聲明,大概就明白和View有什么不同了,ViewGroup繼承于View同時還是實現(xiàn)了ViewParent和ViewManager兩個接口
public abstract class ViewGroup extends View implements ViewParent, ViewManager
先看看ViewManager接口,為ViewGroup提供了管理addView、updateViewLayout和removeView方法操作子View
public interface ViewManager
{
public void addView(View view, ViewGroup.LayoutParams params);
public void updateViewLayout(View view, ViewGroup.LayoutParams params);
public void removeView(View view);
}
而ViewParent則是定義了requestLayout和一些焦點事件的處理方法,高版本的API中還添加了嵌套滑動的一些方法
ViewGroup除了所實現(xiàn)的這兩個接口與View不同以外,還有重要的一點,ViewGroup是抽象類,將View的onLayout重置為抽象方法,也就是ViewGroup的子類必須實現(xiàn)onLayout方法來布局子View。View的onLayout方法是空實現(xiàn),因為對于一個普通的View來說該方法并沒有什么實現(xiàn)價值。
除此之外,在View中比較重的兩個方法onMeasure和onDraw在ViewGroup中都沒有被重寫,相對于onMeasure方法,在ViewGroup中增加了一些計算子View的方法,如measureChildren,measureChildrenWithManager等;而對于onDraw方法,VIewGroup定義了一個dispatchDraw方法來調(diào)其每一個子View的onDraw方法。這樣ViewGroup就是真的像一個容器一樣,器職責只是負責對子元素的操作而非具體行為。
四、總結
組合模式與解釋器模式有一定類同,都涉及遞歸的調(diào)用,但是組合模式所提供的屬性層次結構使得可以同等對待單個對象和對象集合。不過是以犧牲單一原則換來的,而組合模式是通過繼承來實現(xiàn)的,這樣有缺少些的了擴展性。
優(yōu)點:
- 清楚的定義層次,同時可以忽略層次差異,方便對層次結構進行控制
- 高層模塊可以一致的使用一個組合結構或者其中單個對象,不必關心處理的單個對象還是整個組合結構,簡化代碼
- 對于枝干構件和葉子構件的新增很方便
- 通過枝干對象和葉子對象的遞歸組合,可以形成復雜的樹形結構,同時保持簡單的方式進行控制
缺點:
- 對于增加新構件是很對容器中的構件類型進行限制。不能依賴類型系統(tǒng)來施加這些約束,因為它們都來自于相同的抽象層,在這種情況下,必須通過在運行時進行類型檢查來實現(xiàn),這個實現(xiàn)過程較為復雜。