天天看點

組合模式(Composite)

組合模式(Composite):将對象組合成樹形結構以表示“部分-整體”的層次結構,使得使用者對單個對象群組合對象的使用具有一緻性。

使用場景:

1、用于對象的部分-整體層次結構,如樹形菜單、檔案夾菜單、部門組織架構圖等;

2、對使用者隐藏組合對象與單個對象的不同,使得使用者統一地使用組合結構中的所有對象。

通用類圖:

http://img1.51cto.com/attachment/201007/132836881.jpg 說到樹,“資料結構”這門課上都學習過了,樹的周遊問題最重要了。下面按照類圖直接用簡單的代碼實作一番。

// 抽象構件類、節點類
abstract class Component {
	public String name;

	public Component(String name) {
		this.name = name;
	}

	// 公有操作
	public void getName() {
		System.out.println(this.name);
	}
}      
// 樹枝構件類
class Composite extends Component {

	private LinkedList<Component> children;

	public Composite(String name) {
		super(name);
		this.children = new LinkedList<Component>();
	}

	// 添加一個節點,可能是樹枝、葉子
	public void add(Component child) {
		this.children.add(child);
	}

	// 删除一個節點,可能是樹枝、葉子
	public void remove(String child) {
		this.children.remove(child);
	}

	// 擷取子樹
	public LinkedList<Component> getChildren() {
		return this.children;
	}
}      
// 樹葉構件類
class Leaf extends Component {

	public Leaf(String name) {
		super(name);
	}
}      
// 測試類,負責建構整棵樹
public class Client {
	public static void main(String[] args) {

		Composite root = new Composite("樹根");

		Composite branch01 = new Composite("樹枝01");
		Composite branch02 = new Composite("樹枝02");

		root.add(branch01);
		root.add(branch02);

		Component leaf01 = new Leaf("樹葉01");
		Component leaf02 = new Leaf("樹葉02");
		Component leaf03 = new Leaf("樹葉03");
		Component leaf04 = new Leaf("樹葉04");
		Component leaf05 = new Leaf("樹葉05");

		branch01.add(leaf01);
		branch01.add(leaf02);

		branch02.add(leaf03);
		branch02.add(leaf04);
		branch02.add(leaf05);
		
		displayTree(root);

	}

	// 遞歸周遊整棵樹
	public static void displayTree(Composite root) {
		LinkedList<Component> children = root.getChildren();

		for (Component c : children) {
			if (c instanceof Leaf) {
				System.out.print("\t");
				c.getName();
			} else {
				c.getName();
				// 遞歸
				displayTree((Composite)c);
			}
		}
	}
}      

測試結果:

樹枝01
	樹葉01
	樹葉02
樹枝02
	樹葉03
	樹葉04
	樹葉05      

上面的 Client 類建構樹的代碼讓人看了覺得煩,如果整棵樹下來有幾百個節點,這樣子的工作效率太糟糕了。其實,在實際應用中,并不是這樣子手工地建構一棵複雜的樹的,應該是我們已經将整棵樹的節點内容、邏輯關系都存儲在資料庫表中(更重要的是這錄入工作應該不是我們開發人員做的),由于表中的各個節點記錄都儲存有自身的一些相關資訊,包括是否為樹葉、父節點等等,開發人員需要的就是讓程式從資料庫中的表中讀取記錄來建構整棵樹。

此外,上面的代碼中隻能從根節點往下周遊,不能夠從某一節點開始往上周遊,解決這個問題可以在抽象構件類 Component 類中添加一個 parent 屬性,再添加相應 setParent()  、 getParent()方法即可。而關于不同的周遊方法再具體實作一下就完成了。

上面的類圖是屬于安全模式的,因為 Leaf 類不具有 add 、 remove 等方法,這些具體方法是被下置到 Composite 類(樹枝節點類)中去具體實作了。

如果要實作透明模式,類圖如下:

http://img1.51cto.com/attachment/201007/132904206.jpg

差别僅在于将 add 、remove 等方法上升到抽象構件類 Component 中去了。那麼此時 Leaf 類在具體實作時就必須将繼承而來的 add 、remove 等不可用、不合邏輯的方法給注解 Deprecated 掉,并抛出适當的異常,不提供給使用者使用。看起來這種透明模式似乎更加麻煩,沒事找事。其實,這種模式下使得我們在周遊整棵樹的時候可以不進行強制類型轉換。看看上面的

displayTree() 方法,裡面在使用遞歸周遊時就使用到了 (Composite)c 強制類型轉換了。

Leaf 類代碼如下:

// 樹葉構件類
class Leaf extends Component {

	public Leaf(String name) {
		super(name);
	}
	
	@Deprecated // 抛出不支援的操作異常
	public void add(Component child) throws UnsupportedOperationException{
		throws new UnsupportedOperationException();
	}

	@Deprecated
	public void remove(String child) throws UnsupportedOperationException{
		throws new UnsupportedOperationException();
	}

	@Deprecated
	public LinkedList<Component> getChildren() throws UnsupportedOperationException{
		throws new UnsupportedOperationException();
	}
}