設計模式--組合模式Composite(結構型)

benbenxiongyuan發表於2014-04-17

定義:

1.1 定義:Compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly.(將物件組合成樹形結構以表示“部分-整體”的層次結構,使得使用者對單個物件和組合物件的使用具有一致性。)

1.2 通用類圖:[透明模式]

1.3 通用程式碼:

public abstract class Component {
	// 個體和整體都具有的共享
	public void doSomething() {
		// 編寫業務邏輯
	}

	// 增加一個葉子構件或樹枝構件
	public abstract void add(Component component);

	// 刪除一個葉子構件或樹枝構件
	public abstract void remove(Component component);

	// 獲得分支下的所有葉子構件和樹枝構件
	public abstract ArrayList<Component> getChildren();
}

public class Leaf extends Component {
	@Deprecated
	public void add(Component component) throws UnsupportedOperationException {
		// 空實現,直接拋棄一個“不支援請求”異常
		throw new UnsupportedOperationException();
	}

	@Deprecated
	public void remove(Component component)
			throws UnsupportedOperationException {
		// 空實現
		throw new UnsupportedOperationException();
	}

	@Deprecated
	public ArrayList<Component> getChildren()
			throws UnsupportedOperationException {
		// 空實現
		throw new UnsupportedOperationException();
	}

	public void doSomething() {
		System.out.println("我是一片葉!");
	}
}

public class Composite extends Component {
	// 構件容器
	private ArrayList<Component> componentArrayList = new ArrayList<Component>();

	// 增加一個葉子構件或樹枝構件
	public void add(Component component) {
		this.componentArrayList.add(component);
	}

	// 刪除一個葉子構件或樹枝構件
	public void remove(Component component) {
		this.componentArrayList.remove(component);
	}

	// 獲得分支下的所有葉子構件和樹枝構件
	public ArrayList<Component> getChildren() {
		return this.componentArrayList;
	}

	public void doSomething() {
		System.out.println("我是一樹枝!");
	}
}

public class Client {
	public static void main(String[] args) {
		// 建立一個根節點
		Component root = new Composite();
		// 建立一個樹枝構件
		Component branch = new Composite();
		// 建立一個葉子節點
		Component leaf = new Leaf();
		// 建立整體
		root.add(branch);
		branch.add(leaf);
		display(root);
	}

	// 通過遞迴遍歷樹
	public static void display(Component root) {
		root.doSomething();
		for (Component c : root.getChildren()) {
			if (c instanceof Leaf) { // 葉子節點/////////////////////
				c.doSomething();
			} else { // 樹枝節點
				display(c);
			}
		}
	}
}


優點

2.1 高層模組呼叫簡單:樹形機構中所有節點都是Component,高層模組不必關心自己處理的是單個物件還是整個組合結構,簡化了高層模組的程式碼;

2.2 節點自由增加:容易擴充套件,符合OCP,對維護有利。

缺點

3.1 在上述遍歷樹時,需要判斷是葉子還是樹枝,這點若直接通過型別判斷,則會形成“對實現類的依賴”與依賴倒置原則衝突。不過似乎可以通過在Component中新增標識,在子類中實現它。

應用場景

4.1 維護和展示部分-整體關係的場景,如樹形選單、檔案和資料夾管理;

4.2 從一個整體中能夠獨立出部分模組或功能的場景;

4.3 家族關係圖譜(倒推方可)

注意事項

暫無

擴充套件

6.1 組合模式的真實引用:依靠關聯式資料庫的非物件儲存效能,儲存樹形結構。

6.2 安全的組合模式:類圖如下,這種做法將樹枝與葉子完全分開,在遍歷時需要強制型別轉換(因而破壞了依賴倒置原則),也需要型別判斷。可以避免執行間異常。


6.3 組合模式的遍歷:給定一個結點,查詢其父結點。

範例

(如上6.3,樹遍歷查詢)


原始碼如下:(作者原書例)

public abstract class Corp {
	// 公司每個人都有名稱
	private String name = "";
	// 公司每個人都職位
	private String position = "";
	// 公司每個人都有薪水
	private int salary = 0;
	// 父節點是誰
	private Corp parent = null;

	/*
	 * 通過介面的方式傳遞,我們改變一下習慣,傳遞進來的引數名以下劃線開始 這個在一些開源專案中非常常見,一般建構函式都是定義的
	 */
	public Corp(String _name, String _position, int _salary) {
		this.name = _name;
		this.position = _position;
		this.salary = _salary;
	}

	// 獲得員工資訊
	public String getInfo() {
		String info = "";
		info = "姓名:" + this.name;
		info = info + "\t職位:" + this.position;
		info = info + "\t薪水:" + this.salary;
		return info;
	}

	// 設定父節點
	protected void setParent(Corp _parent) {
		this.parent = _parent;
	}

	// 等到父節點
	public Corp getParent() {
		return this.parent;
	}
}

public class Branch extends Corp {
	// 領導下邊有那些下級領導和小兵
	ArrayList<Corp> subordinateList = new ArrayList<Corp>();

	// 建構函式是必須的了
	public Branch(String _name, String _position, int _salary) {
		super(_name, _position, _salary);
	}

	// 增加一個下屬,可能是小頭目,也可能是個小兵
	public void addSubordinate(Corp corp) {
		corp.setParent(this); // 設定父節點
		this.subordinateList.add(corp);
	}

	// 我有哪些下屬
	public ArrayList<Corp> getSubordinate() {
		return this.subordinateList;
	}
}

public class Leaf extends Corp {
	// 就寫一個建構函式,這個是必須的
	public Leaf(String _name, String _position, int _salary) {
		super(_name, _position, _salary);
	}
}

public class Client {
	static Leaf f;

	public static void main(String[] args) {
		// 首先是組裝一個組織結構出來
		Branch ceo = compositeCorpTree();
		// 首先把CEO的資訊列印出來:
		System.out.println(ceo.getInfo());
		System.out.println();
		System.out.println("開發人員 f 的上司是:" + f.getParent().getInfo());
		System.out.println();
		// 然後是所有員工資訊
		System.out.println(getTreeInfo(ceo));
	}

	// 把整個樹組裝出來
	public static Branch compositeCorpTree() {
		// 首先產生總經理CEO
		Branch root = new Branch("王大麻子", "總經理", 100000);
		// 把三個部門經理產生出來
		Branch developDep = new Branch("劉大瘸子", "研發部門經理", 10000);
		Branch salesDep = new Branch("馬二柺子", "銷售部門經理", 20000);
		Branch financeDep = new Branch("趙三駝子", "財務部經理", 30000);
		// 再把三個小組長產生出來
		Branch firstDevGroup = new Branch("楊三乜斜", "開發一組組長", 5000);
		Branch secondDevGroup = new Branch("吳大棒槌", "開發二組組長", 6000);
		// 把所有的小兵都產生出來
		Leaf a = new Leaf("a", "開發人員", 2000);
		Leaf b = new Leaf("b", "開發人員", 2000);
		Leaf c = new Leaf("c", "開發人員", 2000);
		Leaf d = new Leaf("d", "開發人員", 2000);
		Leaf e = new Leaf("e", "開發人員", 2000);
		f = new Leaf("f", "開發人員", 2000);
		Leaf g = new Leaf("g", "開發人員", 2000);
		Leaf h = new Leaf("h", "銷售人員", 5000);
		Leaf i = new Leaf("i", "銷售人員", 4000);
		Leaf j = new Leaf("j", "財務人員", 5000);
		Leaf k = new Leaf("k", "CEO祕書", 8000);
		Leaf zhengLaoLiu = new Leaf("鄭老六", "研發部副經理", 20000);
		// 開始組裝
		// CEO下有三個部門經理和一個祕書
		root.addSubordinate(k);
		root.addSubordinate(developDep);
		root.addSubordinate(salesDep);
		root.addSubordinate(financeDep);
		// 研發部經理
		developDep.addSubordinate(zhengLaoLiu);
		developDep.addSubordinate(firstDevGroup);
		developDep.addSubordinate(secondDevGroup);
		// 看看開發兩個開發小組下有什麼
		firstDevGroup.addSubordinate(a);
		firstDevGroup.addSubordinate(b);
		firstDevGroup.addSubordinate(c);
		secondDevGroup.addSubordinate(d);
		secondDevGroup.addSubordinate(e);
		secondDevGroup.addSubordinate(f);
		// 再看銷售部下的人員情況
		salesDep.addSubordinate(h);
		salesDep.addSubordinate(i);
		// 最後一個財務
		financeDep.addSubordinate(j);
		return root;
	}

	// 遍歷整棵樹,只要給我根節點,我就能遍歷出所有的節點
	public static String getTreeInfo(Branch root) {
		ArrayList<Corp> subordinateList = root.getSubordinate();
		String info = "";
		for (Corp s : subordinateList) {
			if (s instanceof Leaf) { // 是員工就直接獲得資訊
				info = info + s.getInfo() + "\n";
			} else { // 是個小頭目
				info = info + s.getInfo() + "\n" + getTreeInfo((Branch) s);
			}
		}
		return info;
	}
}

測試結果:

姓名:王大麻子 職位:總經理 薪水:100000

開發人員 f 的上司是:姓名:吳大棒槌 職位:開發二組組長 薪水:6000

姓名:k 職位:CEO祕書 薪水:8000

姓名:劉大瘸子 職位:研發部門經理 薪水:10000

姓名:鄭老六 職位:研發部副經理 薪水:20000

姓名:楊三乜斜 職位:開發一組組長 薪水:5000

姓名:a 職位:開發人員 薪水:2000

姓名:b 職位:開發人員 薪水:2000

姓名:c 職位:開發人員 薪水:2000

姓名:吳大棒槌 職位:開發二組組長 薪水:6000

姓名:d 職位:開發人員 薪水:2000

姓名:e 職位:開發人員 薪水:2000

姓名:f 職位:開發人員 薪水:2000

姓名:馬二柺子 職位:銷售部門經理 薪水:20000

姓名:h 職位:銷售人員 薪水:5000

姓名:i 職位:銷售人員 薪水:4000

姓名:趙三駝子 職位:財務部經理 薪水:30000

姓名:j 職位:財務人員 薪水:5000

 

轉自:http://blog.csdn.net/yuanlong_zheng/article/details/7584797

相關文章