說起OO的特點,大家腦海中會立刻蹦出幾個詞:多態(tài)、繼承(is-a)、組合(has-a)。
其中組合既可以作為一個特性,也可以作為一種特定的設計模式,但在兩種場合中的含義有些區(qū)別。
OO特點------組合
當把現(xiàn)實世界的各種物體抽象為類之后,很自然地想到類與類之間還存在不同的關系。重要的兩種就是“is-a”和"has-a"。其中"has-a"就表示了組合,即:
Class Macan extends Prosche{
private Machine machine;
private List<Tire> tires;
private Framework framework;
}
一輛macan首先是一輛保時捷(is-a),而一輛macan有一個車框架,一個發(fā)動機和多個輪子(has-a)。按照這一理念建立起來的類便體現(xiàn)了組合的思想。
下面我們再看看設計模式中的組合是如何實現(xiàn)的。
存在某種類A,它實現(xiàn)了接口I中定義的操作;另外存在類B,它表示了一個包含多個實現(xiàn)I接口的類的集合,且為了使B和它所包含的類的行為一致,B自身也應實現(xiàn)I接口。因此,B所包含的可能是A,也可能是另外一個或多個包含了A的B'
這張圖中MachineComponent是一個虛擬類,而非接口,而Machine和MachineComposite均繼承了該類。另外在MachineComposite中還保存了一個MachineComponent的數(shù)組。這樣就已經(jīng)建立其了組合模式的基本結構。
但是在組合模式中需要特別關注的一點就是:是否有樹形結構,是否有環(huán)形結構。
還是看上圖的isTree函數(shù),來判斷當前是否是一個樹。
在MachineComponent中我們定義:
public boolean isTree(){
return isTree(new HashSet<MachineComponent>());
}
protected abstract boolean isTree(Set<? extends MachineComponent> visited);
而在子類中,我們實現(xiàn)該isTree函數(shù):
protected boolean isTree(Set visited) {
if (visited.contains(this)) return false;
visited.add(this);
for (MachineComponent m : components) {
if (!m.isTree(visited)) return false;
}
return true;
}
可以發(fā)現(xiàn),在isTree我們維護了一個Set,以保證每一個元素只被訪問了一次。如果存在當前元素已經(jīng)在Set中的,isTree函數(shù)便會返回false.
判斷是否為樹結構非常重要,因為當我們對組合進行迭代遍歷時,很可能不允許某一個子類被遍歷多次。例如當前需要計算所有Machine的數(shù)量,如果有兩個組合使用了同一個Machine,在組合進行計數(shù)時便會將其計算兩次,這顯然與實際不符。
另外整個組合中是否有環(huán)也需要仔細考慮。如果A中有B,B中有C,C中又有A,這個時候如果對A進行類似計數(shù)的操作,很可能就會發(fā)生死循環(huán)。如果對其進行類似isTree中訪客記錄的處理,可以避免此問題。
綜上,組合包含了兩個主要的特性:
- 一個組合對象,可以包含對應的單個對象,也可以包含另一個合適的組合對象。
- 組合對象與單個對象應該實現(xiàn)共同的接口或繼承共同的虛類,以獲得相同的行為。