一、基本介紹
組合模式,將對象組合成樹形結構以表示“整體-部分”的層次結構,一種對象結構型模式。
由於在軟件開發中存在大量的樹形結構,因此組合模式是一種使用頻率較高的結構型設計模式,Java SE中的AWT和Swing包的設計就基於組合模式。除此以外,在XML解析、組織結構樹處理、文件系統設計等領域,組合模式都得到了廣泛應用。
組合模式的分類:
1、透明組合模式
透明組合模式中,抽象構件角色中聲明瞭所有對於管理成員對象的方法,透明組合模式是組合模式的標準形式。
2、安全組合模式
安全組合模式中,在抽象構件角色中沒有聲明任何用於管理成員對象的方法,而是在容器構件類中聲明並實現這些方法。
二、組合模式的結構
1、Component(抽象構件)
它可以是接口或抽象類,以葉子構件和容器構件對象聲明接口,在該角色中可以包含所有子類共有行爲的聲明和實現。在抽象構件中定義了訪問及管理它的子構件的方法,如增加子構件、刪除子構件、獲取子構件等。
2、Composite(容器構件)
它在組合結構中表示容器節點對象,容器節點包含子節點,其子節點可以是葉子節點,也可以是容器節點,它提供一個集合用於存儲子節點,實現了抽象構件中定義的行爲,包括哪些訪問及管理子構件的方法,在其業務方法中可以遞歸調用其子節點的業務方法。
3、Leaf(葉子構件)
它在組合結構中表示葉子節點對象,葉子節點沒有子節點,它實現了在抽象構件中定義的行爲。對於那些訪問及管理子構件的方法,可以通過異常等方式進行處理。
組合模式的關鍵是定義了一個抽象構件類,它既可以代表葉子,又可以代表容器,而客戶端針對該抽象構件類進行編程,無須知道它到底表示的是葉子還是容器,可以對其進行統一處理。同時容器對象與抽象構件類之間還建立一個聚合關聯關係,在容器對象中既可以包含葉子,也可以包含容器,以此實現遞歸組合,形成一個樹形結構。
三、組合模式優缺點
1、優點
(1)組合模式可以清晰的定義分層次的複雜對象,表示對象的全部或部分層次,它讓客戶端忽略了層次的差異,方便對整個層次結構進行控制。
(2)客戶端可以一致地使用一個組合結構或其中單個對象,不必關心處理的是單個對象還是整個組合結構,簡化了客戶端代碼。
(3)在組合模式中增加新的容器構件和葉子構件都很方便,無須對現有類庫進行任何修改,符合“開閉原則”。
(4)組合模式爲樹形結構的面向對象實現提供了一種靈活的解決方案,通過葉子對象和容器對象的遞歸組合,可以形成複雜的樹形結構,但對樹形結構的控制卻非常簡單。
2、缺點
(1)使得設計更加複雜,客戶端需要花更多時間理清類之間的層次關係。
(2)在增加新構件時很難對容器中的構件類型進行限制。
四、組合模式的使用場景
1、在具有整體和部分的層次結構中,希望通過一種方式忽略整體與部分的差異,客戶端可以一致地對待它們。
2、在一個使用面嚮對象語言開發的系統中需要處理一個樹形結構。
3、在一個系統中能夠分離出葉子對象和容器對象,而且它們的類型不固定,需要增加一些新的類型。
五、組合模式實現學校院系展示
1、抽象構件
package designMode.advance.composite;
public abstract class OrganizationComponent {
private String name; // 名字
private String des; // 說明
protected void add(OrganizationComponent organizationComponent) {
//默認實現
throw new UnsupportedOperationException();
}
protected void remove(OrganizationComponent organizationComponent) {
//默認實現
throw new UnsupportedOperationException();
}
//構造器
public OrganizationComponent(String name, String des) {
super();
this.name = name;
this.des = des;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDes() {
return des;
}
public void setDes(String des) {
this.des = des;
}
//方法print, 做成抽象的, 子類都需要實現
protected abstract void print();
}
2、容器構件 --> 學校類
package designMode.advance.composite;
import java.util.ArrayList;
import java.util.List;
public class University extends OrganizationComponent {
List<OrganizationComponent> organizationComponents = new ArrayList<OrganizationComponent>();
// 構造器
public University(String name, String des) {
super(name, des);
}
// 重寫add
@Override
protected void add(OrganizationComponent organizationComponent) {
organizationComponents.add(organizationComponent);
}
// 重寫remove
@Override
protected void remove(OrganizationComponent organizationComponent) {
organizationComponents.remove(organizationComponent);
}
@Override
public String getName() {
return super.getName();
}
@Override
public String getDes() {
return super.getDes();
}
// print方法,就是輸出University 包含的學院
@Override
protected void print() {
System.out.println("--------------" + getName() + "--------------");
//遍歷 organizationComponents
for (OrganizationComponent organizationComponent : organizationComponents) {
organizationComponent.print();
}
}
}
3、容器構件 --> 學院類
package designMode.advance.composite;
import java.util.ArrayList;
import java.util.List;
public class College extends OrganizationComponent {
//List 中 存放的Department
List<OrganizationComponent> organizationComponents = new ArrayList<OrganizationComponent>();
// 構造器
public College(String name, String des) {
super(name, des);
}
// 重寫add
@Override
protected void add(OrganizationComponent organizationComponent) {
// 將來實際業務中,Colleage 的 add 和 University add 不一定完全一樣
organizationComponents.add(organizationComponent);
}
// 重寫remove
@Override
protected void remove(OrganizationComponent organizationComponent) {
organizationComponents.remove(organizationComponent);
}
@Override
public String getName() {
return super.getName();
}
@Override
public String getDes() {
return super.getDes();
}
// print方法,就是輸出University 包含的學院
@Override
protected void print() {
System.out.println("--------------" + getName() + "--------------");
//遍歷 organizationComponents
for (OrganizationComponent organizationComponent : organizationComponents) {
organizationComponent.print();
}
}
}
4、葉子節點 --> 專業類
package designMode.advance.composite;
public class Department extends OrganizationComponent {
//沒有集合
public Department(String name, String des) {
super(name, des);
}
//add , remove 就不用寫了,因爲他是葉子節點
@Override
public String getName() {
return super.getName();
}
@Override
public String getDes() {
return super.getDes();
}
@Override
protected void print() {
System.out.println(getName());
}
}
5、測試類
package designMode.advance.composite;
public class Client {
public static void main(String[] args) {
//從大到小創建對象 學校
OrganizationComponent university = new University("清華大學", " 中國頂級大學 ");
//創建 學院
OrganizationComponent computerCollege = new College("計算機學院", " 計算機學院 ");
OrganizationComponent infoEngineercollege = new College("信息工程學院", " 信息工程學院 ");
//創建各個學院下面的系(專業)
computerCollege.add(new Department("軟件工程", " 軟件工程不錯 "));
computerCollege.add(new Department("網絡工程", " 網絡工程不錯 "));
computerCollege.add(new Department("計算機科學與技術", " 計算機科學與技術是老牌的專業 "));
//
infoEngineercollege.add(new Department("通信工程", " 通信工程不好學 "));
infoEngineercollege.add(new Department("信息工程", " 信息工程好學 "));
//將學院加入到 學校
university.add(computerCollege);
university.add(infoEngineercollege);
university.print();
//infoEngineercollege.print();
}
}
六、Java集合類HashMap 源碼分析
HashMap 提供了putAll方法,可以將另一個Map對象放入自己的存儲空間中,如有相同的key則會覆蓋之前的key值所對應的value值。
1、代碼實例
package designMode.advance.composite;
import java.util.HashMap;
import java.util.Map;
public class HashMapComposite {
public static void main(String[] args) {
Map<Integer,String> universityMap = new HashMap<>();
universityMap.put(1,"清華大學");
universityMap.put(2,"北京大學");
universityMap.put(3,"遼寧石油化工大學");
System.out.println("universityMap: " + universityMap);
Map<Integer,String> collegeMap = new HashMap<>();
collegeMap.put(1,"計算機學院");
collegeMap.put(4,"信息工程學院");
System.out.println("collegeMap: " + collegeMap);
universityMap.putAll(collegeMap);
System.out.println("universityMap.putAll(collegeMap),"+universityMap);
}
}
2、控制檯輸出
3、源碼分析
putAll接收的參數爲父類Map類型,所以hashmap是一個容器類,map的子類爲葉子類,當然如果map的其它子類也實現了putAll方法,那麼他們既是容器類,又都是葉子類;
同理,ArrayList 中的 addAll(Collection<? extends E> c) 方法也是一個組合模式的應用;
七、Mybatis SqlNode中的組合模式
MyBatis 的強大特性之一便是它的動態SQL,其通過 if, choose, when, otherwise, trim, where, set, foreach 標籤,可組合成非常靈活的SQL語句,從而提高開發人員的效率。
動態SQL – IF
<select id="findActiveBlogLike" resultType="Blog">
SELECT * FROM BLOG WHERE state = ‘ACTIVE’
<if test="title != null">
AND title like #{title}
</if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
</select>
動態SQL – choose, when, otherwise
<select id="findActiveBlogLike" resultType="Blog">
SELECT * FROM BLOG WHERE state = ‘ACTIVE’
<choose>
<when test="title != null">
AND title like #{title}
</when>
<when test="author != null and author.name != null">
AND author_name like #{author.name}
</when>
<otherwise>
AND featured = 1
</otherwise>
</choose>
</select>
動態SQL – where
<select id="findActiveBlogLike" resultType="Blog">
SELECT * FROM BLOG
<where>
<if test="state != null">
state = #{state}
</if>
<if test="title != null">
AND title like #{title}
</if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
</where>
</select>
動態SQL – foreach
<select id="selectPostIn" resultType="domain.blog.Post">
SELECT * FROM POST P WHERE ID in
<foreach item="item" index="index" collection="list"
open="(" separator="," close=")">
#{item}
</foreach>
</select>
Mybatis在處理動態SQL節點時,應用到了組合設計模式,MyBatis會將映射文件中定義的靜態SQL節點、文本節點等解析成對應的SqlNode實現,形成樹形結構。