天天看點

Java設計模式之組合模式(Composite)一、概述二、組合模式三、透明方式與安全方式四、實戰應用五、總結

一、概述

我們常常可以看到這樣一種形式,比如說電腦中的磁盤管理,我的電腦中有C槽、D盤、E盤,在C槽中又有A檔案夾、B檔案夾等,在A檔案夾下又有A1檔案夾、A2檔案夾、A3檔案等等;再比如說在桌子上有背包、電腦、水杯、書架、筆筒等,在背包裡有書、本子、IPad等,在書架上有書A、書B等,在筆筒裡有中性筆、鋼筆、鉛筆、彩筆等;再比如北京公司總部有财務部、人力部、上海分公司、深圳分公司等,在上海分公司又有财務部、人力部、南京辦事處、杭州辦事處等,在南京辦事處下面又有财務部、人力部等。我這麼說,可能看不出什麼,下面用圖表示一下這3個例子。

Java設計模式之組合模式(Composite)一、概述二、組合模式三、透明方式與安全方式四、實戰應用五、總結

圖1 我的電腦結構示意圖

Java設計模式之組合模式(Composite)一、概述二、組合模式三、透明方式與安全方式四、實戰應用五、總結

圖2 桌面結構示意圖

Java設計模式之組合模式(Composite)一、概述二、組合模式三、透明方式與安全方式四、實戰應用五、總結

圖3 公司結構示意圖 

這樣一看他們結構是不是一目了然,其實就是樹形結構,有根、有枝幹、有葉子。如果這個時候boss抛來一個需求,讓所有的枝幹具有一定的功能A,所有的葉子具有一定的功能B,或者,更具體點,從圖3來說,要求所有公司的财務部具有相同的功能A,人力部具有功能B,這個時候我們要怎麼實作呢。

當當當當~ 組合模式應運而生!

二、組合模式

1. 定義

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

2. 基本結構圖

Java設計模式之組合模式(Composite)一、概述二、組合模式三、透明方式與安全方式四、實戰應用五、總結

Component:抽象類或接口,為組合中的對象聲明接口。通過add和remove方法提供增加和删除樹枝、樹葉的功能。

Leaf:葉子,為組合中最末端的節點,沒有子節點。

Composite:枝節點,上有父節點,下有子節點。定義了枝節點的行為,用來存儲子部件。在Component接口中實作與子部件有關的操作——增加add和删除remove。

3. 基本代碼

Component抽象類

public abstract class Component {
	protected String name;
	
	public Component(String name) {
		this.name = name;
	}
	
	public abstract void add(Component c);
	public abstract void remove(Component c);
	public abstract void display(int depth);
}
           

Leaf葉子類,由于葉子沒有子節點,無需再增葉子和樹枝,是以這裡的add和remove方法就沒有實作。但卻保留了這兩個方法,是為了與樹枝節點Composite類保持一緻的接口,以消除葉節點和枝節點在抽象層次的差別。

public class Leaf extends Component{
	 
	public Leaf(String name) {
		super(name);
	}
	
	@Override
	public void add(Component c) {
		
	}

	@Override
	public void remove(Component c) {
		
	}

	@Override
	public void display(int depth) {
		for(int i=0;i<depth;i++) {
			System.out.print("-");
		}
		System.out.println(name);
	}

}
           

Composite枝節點類

public class Composite extends Component{
	private List<Component> children = new ArrayList<Component>();
	
	public Composite(String name) {
		super(name);
	}
	
	@Override
	public void add(Component c) {
		children.add(c);
	}

	@Override
	public void remove(Component c) {
		if(children.contains(c)) {
			children.remove(c);
		}
	}

	@Override
	public void display(int depth) {
		for(int i=0;i<depth;i++) {
			System.out.print("-");
		}
		System.out.println(name);
		for (Component component : children) {
			component.display(depth+2);
		}
	}

}
           

測試類

public class CompositeTest {

	public static void main(String[] args) {
		//建立根節點
		Composite root = new Composite("Root");
		//添加兩個葉節點
		root.add(new Leaf("Leaf A"));
		root.add(new Leaf("Leaf B"));
		
		//建立枝節點
		Composite c1 = new Composite("Composite X");
		//給枝節點X添加兩個葉節點
		c1.add(new Leaf("Leaf XA"));
		c1.add(new Leaf("Leaf XB"));
		
		//将枝節點X添加到根節點上
		root.add(c1);
		
		//建立枝節點
		Composite c2 = new Composite("Composite XY");
		//給枝節點XY添加兩個葉節點
		c2.add(new Leaf("Leaf XYA"));
		c2.add(new Leaf("Leaf XYB"));
		
		//将枝節點XY添加到枝節點X上
		c1.add(c2);
		
		//給根節點添加葉節點C
		root.add(new Leaf("Leaf C"));
		
		//建立葉節點D
		Leaf d = new Leaf("Leaf D");
		//将葉節點D添加到根節點上
		root.add(d);
		//将葉節點D從根節點移除
		root.remove(d);
		
		//顯示樹狀結構結果
		root.display(1);
	}

}
           

結果:

Java設計模式之組合模式(Composite)一、概述二、組合模式三、透明方式與安全方式四、實戰應用五、總結

用一張圖來表示這個結果的話,如下

Java設計模式之組合模式(Composite)一、概述二、組合模式三、透明方式與安全方式四、實戰應用五、總結

進一步說明,這個例子裡的display方法隻是為了輸出結果顯示出比較明确的層次結構,實際應用中可以根據需求做相應調整。

三、透明方式與安全方式

1. 透明方式

 上面例子裡,葉節點Leaf類裡面的add和remove方法其實是無意義的方法,沒有實作内容,這種方式叫做透明方式。

即,在Component接口中聲明所有用來管理子對象的方法,包括add和remove等,這樣實作Component接口的所有子類都具備了add和remove方法。

這樣做的優點,上面也提到過了,就是讓枝節點和葉節點對外界來說沒有差別,具備完全一緻的行為接口。

相應的缺點就是,葉節點實作了完全沒有任何意義的add和remove方法。

2. 安全方式

與透明方式相對應,如果葉節點Leaf類中不用add和remove方法,這種方式就叫做安全方式。

即,在Component接口中不聲明add和remove方法,而是在子部件Composite中聲明所有用來管理子類對象的方法。

這樣做的優點就是上面提到的透明方式的缺點,這裡不會在Leaf類中出現無意義的方法。

相應的缺點也很明顯,就是在用戶端調用時需要做判斷進行區分,稍稍麻煩一些。

四、實戰應用

這裡就用上面的公司結構來舉例說明好了

抽象公司類Company,就是上面的Component

public abstract class Company {
	protected String name;
	
	public Company(String name) {
		this.name = name;
	}
	
	public abstract void add(Company company);
	public abstract void remove(Company company);
	public abstract void display(int depth);
	public abstract void duty();
}
           

具體公司實作類ConcreteCompany,就是上面的Composite

public class ConcreteCompany extends Company{
	//聲明公司集合
	private List<Company> companies = new ArrayList<Company>();
	
	public ConcreteCompany(String name) {
		super(name);
	}
	
	/**
	 * 添加子公司
	 */
	@Override
	public void add(Company company) {
		companies.add(company);
	}

	/**
	 * 删除子公司
	 */
	@Override
	public void remove(Company company) {
		if (companies.contains(company)) {
			companies.remove(company);
		}
	}

	/**
	 * 顯示
	 */
	@Override
	public void display(int depth) {
		for(int i=0;i<depth;i++) {
			System.out.print("-");
		}
		System.out.println(name);
		for (Company company : companies) {
			company.display(depth+2);
		}		
	}

	/**
	 * 公司職責
	 */
	@Override
	public void duty() {
		for (Company company : companies) {
			company.duty();
		}		
	}

}
           

人力部HRCompany,就是上面的葉子類Leaf

/**
 * 人力資源部
 */
public class HRDepartment extends Company{

	public HRDepartment(String name) {
		super(name);
	}

	@Override
	public void add(Company company) {
		
	}

	@Override
	public void remove(Company company) {
		
	}

	@Override
	public void display(int depth) {
		for(int i=0;i<depth;i++) {
			System.out.print("-");
		}
		System.out.println(name);
	}

	@Override
	public void duty() {
		System.out.println(name+"職責:招聘人才");
	}

}
           

财務部FinanceCompany,就是上面的Leaf。這個例子中有兩個Leaf——人力部和财務部,它們都沒有子節點,符合葉子特征,但是由于這裡人力部和财務部具有不同的公司職責,是以分開兩個實作。

/**
 * 财務部
 */
public class FinanceDepartment extends Company{

	public FinanceDepartment(String name) {
		super(name);
	}

	@Override
	public void add(Company company) {
		
	}

	@Override
	public void remove(Company company) {
		
	}

	@Override
	public void display(int depth) {
		for(int i=0;i<depth;i++) {
			System.out.print("-");
		}
		System.out.println(name);
	}

	@Override
	public void duty() {
		System.out.println(name+"職責:公司财務管理");
	}

}
           

測試類

public class CompositeTest {

	public static void main(String[] args) {
		ConcreteCompany root = new ConcreteCompany("北京總公司");
		
		root.add(new HRDepartment("總公司人力部"));
		root.add(new FinanceDepartment("總公司财務部"));
		
		ConcreteCompany c1 = new ConcreteCompany("上海分公司");
		c1.add(new HRDepartment("上海分公司人力部"));
		c1.add(new FinanceDepartment("上海分公司财務部"));
		
		root.add(c1);
		
		ConcreteCompany c2 = new ConcreteCompany("南京辦事處");
		c2.add(new HRDepartment("南京辦事處人力部"));
		c2.add(new FinanceDepartment("南京辦事處财務部"));
		
		c1.add(c2);
		
		ConcreteCompany c3 = new ConcreteCompany("杭州辦事處");
		c3.add(new HRDepartment("杭州辦事處人力部"));
		c3.add(new FinanceDepartment("杭州辦事處财務部"));
		
		c1.add(c3);
		
		System.out.println("各公司名稱:");
		root.display(1);
		
		System.out.println("\n各部門職責:");
		root.duty();
	}

}
           

結果

Java設計模式之組合模式(Composite)一、概述二、組合模式三、透明方式與安全方式四、實戰應用五、總結

五、總結

使用場合:

(1)如果各對象展現出“部分-整體”層次結構(或樹形結構)時,可以考慮使用組合模式。

(2)當客戶希望忽略單個對象與組合對象的差別,可以使用統一接口調用時,可以考慮使用組合模式。

組合模式,簡單易用,核心其實就是一個遞歸群組合,展開來說就是一個很複雜的對象由許多基本對象構成,其中基本對象又可以構成稍複雜一些的組合對象,而組合對象又可以被重組成更複雜的組合對象,如此遞歸下去就構成了最終的對象。

寫在最後,

本文主要是小貓看了《大話設計模式》的一些記錄筆記,再加之自己的一些了解整理出此文,友善以後查閱,僅供參考。