設計模式系列之「組合模式」

小Y科普: 家譜又稱族譜、宗譜等。它以記載父系家族世系、人物爲中心,由正史中的帝王本紀及王侯列傳、年表等演變而來。是一種特殊的文獻,就其內容而言,是中國五千年文明史中具備平民特點的文獻,記載的是同宗共祖血緣集團世系人物和事蹟等方面狀況的歷史圖籍。編程

Now,how to 實現 小J's 族譜。

1、小J族譜簡略版

從最頂層的第一代J開始,一代代往下記錄下去,這很明顯就是一個樹狀結構,如今小Y要作的就是經過最合適的方式把小J的族譜圖遍歷出來。安全

2、樹狀圖的拆解利器-組合模式

1.組合模式的定義

組合模式也叫合成模式,有時又叫作部分-總體模式,主要是用來描述部分與總體的關係,將對象組合成樹形結構以表示「部分-總體」的層次結構,使得用戶對單個對象和組合對象的使用具備一致性。函數

2.組合模式的角色介紹(組合模式有兩種實現:安全模式和透明模式)

  • Component抽象構件角色
    定義參加組合對象的共有方法和屬性,能夠定義一些默認的行爲或屬性。this

  • Leaf葉子構件
    Leaf葉子構件葉子對象,其下再也沒有其餘的分支,也就是遍歷的最小單位。spa

  • Composite樹枝構件
    樹枝對象,它的做用是組合樹枝節點和葉子節點造成一個樹形結構。組合模式的重點就在樹枝構件。code

3.組合模式的使用場景

  • 只要是樹形結構或者只要是要體現局部和總體的關係的時候,並且這種關係還可能比較深,就要考慮一下組合模式。component

  • 從一個總體中可以獨立出部分模塊或功能的場景。cdn

  • 維護和展現部分-總體關係的場景。對象

4.安全模式和透明模式的具體實現

(1)安全模式blog

①抽象構件

public abstract class Component {
	//個體和總體都具備
	public void operation(){
		//編寫業務邏輯
	}
}
複製代碼

②樹枝構件

public class Composite extends Component {
	//構件容器
	private List<Component> componentArrayList = new ArrayList<Component>();
	//增長一個葉子構件或樹枝構件
	public void add(Component component){
		this.componentArrayList.add(component);
	}
	//刪除一個葉子構件或樹枝構件
	public void remove(Component component){
		this.componentArrayList.remove(component);
	}
	//得到分支下的全部葉子構件和樹枝構件
	public List<Component> getChildren(){
		return this.componentArrayList;
	}
}
複製代碼

③樹葉構件

public class Leaf extends Component {
	/*
	* 能夠覆寫父類方法
	* public void operation(){
	*
	* }
	*/
}
複製代碼

④Client

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

	//經過遞歸遍歷樹
	public static void showTree(Composite root){
		for(Component c:root.getChildren()){
			if(c instanceof Leaf){ //葉子節點
				c.operation();
			}else{ //樹枝節點
				showTree((Composite)c);
			}
		}
	}
}
複製代碼

(2)透明模式

①抽象構件

public abstract class Component {
	//個體和總體都具備
	public void operation(){
		//編寫業務邏輯
	}
	//增長一個葉子構件或樹枝構件
	public abstract void add(Component component);
	//刪除一個葉子構件或樹枝構件
	public abstract void remove(Component component);
	//得到分支下的全部葉子構件和樹枝構件
	public abstract List<Component> getChildren();
}
複製代碼

②樹枝構件

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 List<Component> getChildren(){
		return this.componentArrayList;
	}
}
複製代碼

③樹葉構件

public class Leaf extends Component {

	public void add(Component component){
		//空實現
	}

	public void remove(Component component){
		//空實現
	}

	public List<Component> getChildren(){
		//空實現
	}
}
複製代碼

④Client

public class Client {

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

	//經過遞歸遍歷樹
	public static void showTree(Component root){
		for(Component c:root.getChildren()){
			if(c instanceof Leaf){ //葉子節點
				c.operation();
			}else{ //樹枝節點
				showTree(c);
			}
		}
	}
}
複製代碼

4.安全模式和透明模式的區別

  • 安全模式在抽象組件中只定義一些默認的行爲或屬性,它是把樹枝節點和樹葉節點完全分開;透明模式是把用來組合使用的方法放到抽象類中,無論葉子對象仍是樹枝對象都有相同的結構,經過判斷確認是葉子節點仍是樹枝節點,若是處理不當,這個會在運行期出現問題,不是很建議的方式。

  • 安全模式與依賴倒置原則衝突;透明模式的好處就是它基本遵循了依賴倒轉原則,方便系統進行擴展。

  • 安全模式在遍歷樹形結構的的時候須要進行強制類型轉換;在透明模式下,遍歷整個樹形結構是比較容易的,不用進行強制類型轉換。

3、經過安全模式實現遍歷小J的族譜

①抽象構件抽象族員類

public abstract class PersonMode {
	//人名
	private String name;
	//性別
	private String sex;
	//年齡
	private int age;

	public PersonMode(String name, String sex, int age) {
		this.name = name;
		this.sex = sex;
		this.age = age;
	}

	//我的信息
	public String getPersonInfo(){
		String info="姓名:"+name+"\t性別:"+sex+"\t年齡:"+age;
		return info;
	}
}
複製代碼

②樹葉構件

public class PersonLeaf extends PersonMode {
	//寫一個構造函數
	public PersonLeaf(String name, String sex, int age) {
		super(name, sex, age);
	}
}
複製代碼

③樹枝構件

public class PersonBranch extends PersonMode {
	private List<PersonMode> personModeList=new ArrayList<>();

	public PersonBranch(String name, String sex, int age) {
		super(name, sex, age);
	}

	public void addPerson(PersonMode person){
		this.personModeList.add(person);
	}

	public List<PersonMode> getPersonModeList(){
		return this.personModeList;
		}
}
複製代碼

④Client

public class Client {
	public static void main(String[] args) {
		/**
		 * 組裝小J的族譜
	 	*/
		PersonBranch personBranch=getPersonInfo();
		showTree(personBranch);
	}

	private static PersonBranch getPersonInfo(){
		//第一代J
		PersonBranch OneJ=new PersonBranch("第一代J","男",150);
		//第一代J的三個兒子
		PersonBranch JA=new PersonBranch("JA","男",70);
		PersonBranch JB=new PersonBranch("JB","男",60);
		PersonBranch JC=new PersonBranch("JC","男",50);
		//JA的三個兒子
		PersonBranch JA1=new PersonBranch("JA1","男",40);
		PersonBranch JA2=new PersonBranch("JA2","男",30);
		PersonBranch JA3=new PersonBranch("JA3","男",45);
		//JB的兩個兒子
		PersonBranch JB1=new PersonBranch("JB1","男",40);
		PersonBranch JB2=new PersonBranch("JB2","男",30);
		//JC的兒子小J
		PersonBranch xiao_J=new PersonBranch("xiao_J","男",20);
		//JA1三個兒子
		PersonBranch JA1_1=new PersonBranch("JA1_1","男",18);
		PersonBranch JA1_2=new PersonBranch("JA1_2","男",16);
		PersonBranch JA1_3=new PersonBranch("JA1_3","男",20);
		//JA3三個兒子
		PersonBranch JA3_1=new PersonBranch("JA3_1","男",16);
		PersonBranch JA3_2=new PersonBranch("JA3_2","男",20);
		PersonBranch JA3_3=new PersonBranch("JA3_3","男",18);

		//開始組裝樹狀族譜
		//組裝第一代J下的三個兒子
		OneJ.addPerson(JA);
		OneJ.addPerson(JB);
		OneJ.addPerson(JC);
		//組裝JA的三個兒子
		JA.addPerson(JA1);
		JA.addPerson(JA2);
		JA.addPerson(JA3);
		//組裝JB的兩個兒子
		JB.addPerson(JB1);
		JB.addPerson(JB2);
		//組裝JC的兒子
		JC.addPerson(xiao_J);
		//組裝JA1的三個兒子
		JA1.addPerson(JA1_1);
		JA1.addPerson(JA1_2);
		JA1.addPerson(JA1_3);
		//組裝JA3的三個兒子
		JA3.addPerson(JA3_1);
		JA3.addPerson(JA3_2);
		JA3.addPerson(JA3_3);

		return OneJ;
	}

	//經過遞歸遍歷樹
	private static void showTree(PersonBranch root){
		System.out.println(root.getPersonInfo());
		for(PersonMode c:root.getPersonModeList()){
			if(c instanceof PersonLeaf){ //葉子節點
				System.out.println(c.getPersonInfo());
			}else{ //樹枝節點
				showTree((PersonBranch) c);
			}
		}
	}
}
複製代碼

場景類負責樹狀結構的創建,並能夠經過遞歸方式遍歷整個樹。

輸出的結果爲:

姓名:第一代J 性別:男 年齡:150
姓名:JA 性別:男 年齡:70
姓名:JA1 性別:男 年齡:40
姓名:JA1_1 性別:男 年齡:18
姓名:JA1_2 性別:男 年齡:16
姓名:JA1_3 性別:男 年齡:20
姓名:JA2 性別:男 年齡:30
姓名:JA3 性別:男 年齡:45
姓名:JA3_1 性別:男 年齡:16
姓名:JA3_2 性別:男 年齡:20
姓名:JA3_3 性別:男 年齡:18
姓名:JB 性別:男 年齡:60
姓名:JB1 性別:男 年齡:40
姓名:JB2 性別:男 年齡:30
姓名:JC 性別:男 年齡:50
姓名:xiao_J 性別:男 年齡:20
複製代碼

4、組合模式優缺點

1.優勢

高層模塊調用簡單。局部和總體對調用者來講沒有任何區別,也就是說,高層模塊沒必要關心本身處理的是單個對象仍是整個組合結構,簡化了高層模塊的代碼。

節點自由增長。使用了組合模式後,咱們能夠看看,若是想增長一個樹枝節點、樹葉節點十分簡單,只要找到它的父節點就成,很是容易擴展,符合開閉原則,對之後的維護很是有利。

2.缺點

組合模式有一個很是明顯的缺點,在上面的場景類能夠看到樹枝樹葉直接使用了實現類,這在面向接口編程上是很不恰當的,與依賴倒置原則衝突,它限制了你接口的影響範圍。

5、總結

只要是樹形結構或者只要是要體現局部和總體的關係的時候,並且這種關係還可能比較深,,就要考慮使用組合模式。

Android技術交流吧
相關文章
相關標籤/搜索