一毕莱、簡述
組合模式(Composite Pattern),也稱作部分整體模式(Part-Whole Pattern)卜范,將一組相似的對象看做一個對象處理隧土,并根據(jù)一個樹狀結(jié)構(gòu)來組合對象;對象都提供一個統(tǒng)一的方法去訪問相應(yīng)的對象來處理多個對象的同一性問題藕甩。
組合模式屬于結(jié)構(gòu)設(shè)計模式之一施敢,而其設(shè)計目的就是將對象組合成樹形結(jié)構(gòu)以表示"部分-整體"的層次結(jié)構(gòu)周荐,使得用戶對單個對象和組合對象的使用具有一致性;所以決定組合模式的設(shè)計基礎(chǔ)就是樹狀結(jié)構(gòu)僵娃,組合模式所適用的情況也就是樹狀結(jié)構(gòu)或者適合適用樹狀結(jié)構(gòu)來解決問題的情況概作。
- Component:抽象節(jié)點(diǎn),為組合中的對象聲明統(tǒng)一接口
- Composite:可以儲存子節(jié)點(diǎn)的節(jié)點(diǎn)對象默怨,并實(shí)現(xiàn)抽象節(jié)點(diǎn)的有關(guān)操作
- Leaf:葉子節(jié)點(diǎn)讯榕,沒有子節(jié)點(diǎn)對象
- Client:組合節(jié)點(diǎn)對象,進(jìn)行操作
這里所描述的是透明的組合模式匙睹,可以看到Component
類中除了統(tǒng)一的操作方法doSomthing()
方法以外愚屁,還有操作子節(jié)點(diǎn)的相關(guān)方法,而葉子節(jié)點(diǎn)Leaf
類定義就是沒有葉子節(jié)點(diǎn)的痕檬,顯然這些操作子節(jié)點(diǎn)的方法就是多余的霎槐。
如果要讓Leaf
類不繼承這些方法,只能將Component
類中的這些方法放到它的子類Composite
中梦谜;然而這樣的設(shè)計方式與依賴倒置原則相違背丘跌,所以這里并沒有采用這種組合模式,即安全的組合模式唁桩。
二闭树、實(shí)現(xiàn)
說到組合模式,最適合的就是文件系統(tǒng)的結(jié)構(gòu)了荒澡,文件夾中就子文件夾和文件报辱,子文件夾中可能又是如此,典型的樹狀結(jié)構(gòu)仰猖。
抽象的目錄類捏肢,有目錄名,有輸出目錄名饥侵,并提供添加目錄鸵赫、刪除目錄以及清空目錄的的功能方法
public abstract class Directory {
//當(dāng)前目錄名
private final String name;
public Directory(String name) {
this.name = name;
}
/**
* 輸出目錄結(jié)構(gòu)
*/
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;
}
}
文件夾類,申明一個集合儲存自身所報的目錄躏升,實(shí)現(xiàn)了具體的目錄操作方法辩棒,在print()
方法中循環(huán)調(diào)用集合中目錄的print()
方法輸出
public class Folder extends Directory {
/**
* 當(dāng)前文件夾下的所有目錄元素
*/
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;
}
}
文件類,實(shí)現(xiàn)了print()
方法膨疏,由于沒有子目錄一睁,相關(guān)操作的方法都拋出異常
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盤的結(jié)構(gòu)
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();
}
}
輸出結(jié)果:
C[windows[], Program File(x86)[Intellij[], cache], windows[], log.txt, null.txt]
三佃却、在Android中的實(shí)現(xiàn)
組合模式在Android
的View
和ViewGroup
的嵌套組合使用中得到了很好地展現(xiàn)者吁。采用的事安全的組合模式
省略了View
和ViewGroup
的很多方法,在Android
中饲帅,只有ViewGroup
才能放View
(ViewGroup
也是View
)复凳,View
并不是容器所以ViewGroup
相對于View
多了幾個操作視圖的方法瘤泪。
看看ViewGroup
的聲明,大概就明白和View有什么不同了育八,ViewGroup
繼承于View
同時還是實(shí)現(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
和一些焦點(diǎn)事件的處理方法髓棋,高版本的API中還添加了嵌套滑動的一些方法
ViewGroup
除了所實(shí)現(xiàn)的這兩個接口與View不同以外实檀,還有重要的一點(diǎn),ViewGroup
是抽象類按声,將View
的onLayout
重置為抽象方法膳犹,也就是ViewGroup
的子類必須實(shí)現(xiàn)onLayout
方法來布局子View
。View
的onLayout
方法是空實(shí)現(xiàn)签则,因?yàn)閷τ谝粋€普通的View
來說該方法并沒有什么實(shí)現(xiàn)價值镣奋。
除此之外,在View中比較重的兩個方法onMeasure
和onDraw
在ViewGroup
中都沒有被重寫怀愧,相對于onMeasure
方法,在ViewGroup
中增加了一些計算子View
的方法余赢,如measureChildren
芯义,measureChildrenWithManager
等;而對于onDraw
方法妻柒,VIewGroup
定義了一個dispatchDraw
方法來調(diào)其每一個子View
的onDraw
方法扛拨。這樣ViewGroup
就是真的像一個容器一樣,器職責(zé)只是負(fù)責(zé)對子元素的操作而非具體行為举塔。
四绑警、總結(jié)
組合模式與解釋器模式有一定類同,都涉及遞歸的調(diào)用央渣,但是組合模式所提供的屬性層次結(jié)構(gòu)使得可以同等對待單個對象和對象集合计盒。不過是以犧牲單一原則換來的,而組合模式是通過繼承來實(shí)現(xiàn)的芽丹,這樣有缺少些的了擴(kuò)展性北启。
優(yōu)點(diǎn):
- 清楚的定義層次,同時可以忽略層次差異拔第,方便對層次結(jié)構(gòu)進(jìn)行控制
- 高層模塊可以一致的使用一個組合結(jié)構(gòu)或者其中單個對象咕村,不必關(guān)心處理的單個對象還是整個組合結(jié)構(gòu),簡化代碼
- 對于枝干構(gòu)件和葉子構(gòu)件的新增很方便
- 通過枝干對象和葉子對象的遞歸組合蚊俺,可以形成復(fù)雜的樹形結(jié)構(gòu)懈涛,同時保持簡單的方式進(jìn)行控制
缺點(diǎn):
- 對于增加新構(gòu)件是很對容器中的構(gòu)件類型進(jìn)行限制。不能依賴類型系統(tǒng)來施加這些約束泳猬,因?yàn)樗鼈兌紒碜杂谙嗤某橄髮优疲谶@種情況下宇植,必須通過在運(yùn)行時進(jìn)行類型檢查來實(shí)現(xiàn),這個實(shí)現(xiàn)過程較為復(fù)雜价匠。