設(shè)計模式系列之組合模式及其在JDK和MyBatis源碼中的運用詳解
組合模式及其在JDK源碼中的運用 前言組合和聚合什么是組合模式示例透明組合模式透明組合模式的缺陷安全組合模式 組合模式角色組合模式在JDK源碼中的體現(xiàn)組合模式應(yīng)用場景享元模式優(yōu)缺點總結(jié)
前言
本文主要會講述組合模式的用法,并會結(jié)合在JDK和MyBatis源碼中的運用來進一步理解組合模式。
在編碼原則中,有一條是:多用組合,少用繼承。當(dāng)然這里的組合和我們今天要講的組合模式并不等價,這里的組合其實就是一種聚合,那么聚合和組合有什么區(qū)別呢?
組合和聚合
人在一起叫團伙,心在一起叫團隊。用這句話來詮釋組合與聚合的區(qū)別是相對恰當(dāng)?shù)摹?/p>
聚合就是說各個對象聚合在一起工作,但是我沒有你也行,我照樣可以正常運行。但是組合呢,關(guān)系就比較密切,組合中的各個對象之間組成了一個整體,缺少了某一個對象就不能正常運行或者說功能會有很大缺陷。
也就是說聚合對象不具備相同生命周期,而組合的對象具有相同的生命周期
舉個例子:
比如說電腦和U盤就是聚合,而電腦顯示器和主機就是組合。
什么是組合模式
組合模式(Composite Pattern)也稱之為整體-部分(Part-Whole)模式。組合模式的核心是通過將單個對象(葉子節(jié)點)和組合對象(樹枝節(jié)點)用相同的接口進行表示,使得單個對象和組合對象的使用具有一致性。組合模式屬于結(jié)構(gòu)型模式。
組合模式一般用來描述整體與部分的關(guān)系,它將對象組織到樹形結(jié)構(gòu)中,最頂層的節(jié)點稱為根節(jié)點,根節(jié)點下面可以包含樹枝節(jié)點和葉子節(jié)點,樹枝節(jié)點下面又可以包含樹枝節(jié)點和葉子節(jié)點如下圖所示:

講了這么多,感覺有點抽象,所以依然是老規(guī)矩:Talk is cheap,Show you the code。
示例
組合模式有兩種寫法,分別是透明模式和安全模式。下面我們就以高考的科目為例來看看組合模式是如何體現(xiàn)在代碼中的
透明組合模式
1、首先建立一個頂層的抽象科目類,這個類中定義了三個通用操作方法,但是均默認不支持操作
package com.zwx.design.pattern.composite.transparency;
/**
* 頂層抽象組件
*/
public abstract class GkAbstractCourse {
public void addChild(GkAbstractCourse course){
System.out.println("不支持添加操作");
}
public String getName() throws Exception {
throw new Exception("不支持獲取名稱");
}
public void info() throws Exception{
throw new Exception("不支持查詢信息操作");
}
}
PS:這個類中的公共方法之所以不定義為抽象方法的原因是因為假如定義為抽象方法,那么所有的子類都必須重寫父類方法,這樣體現(xiàn)不出差異性。而這種通過拋異常的方式,如果子類需要用到的功能就重寫覆蓋父類方法即可。
2、新建一個普通科目類繼承通用科目抽象類,這個類作為葉子節(jié)點,沒有重寫addChild方法,也就是這個類屬于葉子節(jié)點,不支持添加子節(jié)點:
package com.zwx.design.pattern.composite.transparency;
/**
* 普通科目類(葉子節(jié)點)
*/
public class CommonCource extends GkAbstractCourse {
private String name;//課程名稱
private String score;//課程分數(shù)
public CommonCource(String name, String score) {
this.name = name;
this.score = score;
}
@Override
public String getName(){
return this.name;
}
@Override
public void info() {
System.out.println("課程:" + this.name + ",分數(shù):" + score);
}
}
3、建立一個具有層級的節(jié)點,三個方法都重寫了,支持添加子節(jié)點,這個類里面為了方便打印的時候看出層級關(guān)系,所以我定義了一個層級屬性。
package com.zwx.design.pattern.composite.transparency;
import java.util.ArrayList;
import java.util.List;
/**
* 樹枝節(jié)點
*/
public class LevelCource extends GkAbstractCourse{
private List<GkAbstractCourse> courseList = new ArrayList<>();
private String name;
private int level;
public LevelCource(String name, int level) {
this.name = name;
this.level = level;
}
@Override
public void addChild(GkAbstractCourse course) {
courseList.add(course);
}
@Override
public String getName(){
return this.name;
}
@Override
public void info() throws Exception {
System.out.println("課程:" + this.name);
for (GkAbstractCourse course : courseList){
for (int i=0;i<level;i++){
System.out.print(" ");
}
System.out.print(">");
course.info();
}
}
}
4、建立一個測試類來測試一下:
package com.zwx.design.pattern.composite.transparency;
public class TestTransparency {
public static void main(String[] args) throws Exception {
GkAbstractCourse ywCourse = new CommonCource("語文","150");
GkAbstractCourse sxCourse = new CommonCource("數(shù)學(xué)","150");
GkAbstractCourse yyCourse = new CommonCource("英語","150");
GkAbstractCourse wlCourse = new CommonCource("物理","110");
GkAbstractCourse hxCourse = new CommonCource("化學(xué)","100");
GkAbstractCourse swCourse = new CommonCource("生物","90");
GkAbstractCourse lzCourse = new LevelCource("理綜",2);
lzCourse.addChild(wlCourse);
lzCourse.addChild(hxCourse);
lzCourse.addChild(swCourse);
GkAbstractCourse gkCourse = new LevelCource("理科高考科目",1);
gkCourse.addChild(ywCourse);
gkCourse.addChild(sxCourse);
gkCourse.addChild(yyCourse);
gkCourse.addChild(lzCourse);
gkCourse.info();
}
}
輸出結(jié)果:
課程:理科高考科目
>課程:語文,分數(shù):150
>課程:數(shù)學(xué),分數(shù):150
>課程:英語,分數(shù):150
>課程:理綜
>課程:物理,分數(shù):110
>課程:化學(xué),分數(shù):100
>課程:生物,分數(shù):90
這里如果用普通科目去調(diào)用add方法就會拋出異常,假如上面調(diào)用:
swCourse.addChild(ywCourse);
會輸出
不支持添加操作
因為在普通科目類里面并沒有重寫addChild方法。
透明組合模式的缺陷
透明模式的特點就是將組合對象所有的公共方法都定義在了抽象組件內(nèi),這樣做的好處是客戶端無需分辨當(dāng)前對象是屬于樹枝節(jié)點還是葉子節(jié)點,因為它們具備了完全一致的接口,不過缺點就是葉子節(jié)點得到到了一些不屬于它的方法,比如上面的addChild方法,這違背了接口隔離性原則。
安全組合模式
安全組合模式只是規(guī)定了系統(tǒng)各個層次的最基礎(chǔ)的一致性行為,而把組合(樹節(jié)點)本身的方法(如樹枝節(jié)點管理子類的addChild等方法)放到自身當(dāng)中。
1、首先還是建立一個頂層的抽象根節(jié)點(這里面只定義了一個通用的抽象info方法):
package com.zwx.design.pattern.composite.safe;
package com.zwx.design.pattern.composite.safe;
/**
* 頂層抽象組件
*/
public abstract class GkAbstractCourse {
protected String name;
protected String score;
public GkAbstractCourse(String name, String score) {
this.name = name;
this.score = score;
}
public abstract void info();
}
2、建立一個葉子節(jié)點(這里只是重寫了info方法,沒有定義其他特有方法):
package com.zwx.design.pattern.composite.safe;
/**
* 葉子節(jié)點
*/
public class CommonCource extends GkAbstractCourse {
public CommonCource(String name,String score) {
super(name,score);
}
@Override
public void info() {
System.out.println("課程:" + this.name + ",分數(shù):" + this.score);
}
}
3、定義一個樹枝節(jié)點(這個類當(dāng)中定義了一個樹枝特有的方法addChild):
package com.zwx.design.pattern.composite.safe;
import java.util.ArrayList;
import java.util.List;
/**
* 樹枝節(jié)點
*/
public class LevelCource extends GkAbstractCourse{
private List<GkAbstractCourse> courseList = new ArrayList<>();
private int level;
public LevelCource(String name, String score,int level) {
super(name,score);
this.level = level;
}
public void addChild(GkAbstractCourse course) {
courseList.add(course);
}
@Override
public void info() {
System.out.println("課程:" + this.name + ",分數(shù):" + this.score);
for (GkAbstractCourse course : courseList){
for (int i=0;i<level;i++){
System.out.print(" ");
}
System.out.print(">");
course.info();
}
}
}
4、新建測試類來測試:
package com.zwx.design.pattern.composite.safe;
public class TestSafe {
public static void main(String[] args) throws Exception {
CommonCource ywCourse = new CommonCource("語文","150");
CommonCource sxCourse = new CommonCource("數(shù)學(xué)","150");
CommonCource yyCourse = new CommonCource("英語","150");
CommonCource wlCourse = new CommonCource("物理","110");
CommonCource hxCourse = new CommonCource("化學(xué)","100");
CommonCource swCourse = new CommonCource("生物","90");
LevelCource lzCourse = new LevelCource("理綜","300",2);
lzCourse.addChild(wlCourse);
lzCourse.addChild(hxCourse);
lzCourse.addChild(swCourse);
LevelCource gkCourse = new LevelCource("理科高考","750",1);
gkCourse.addChild(ywCourse);
gkCourse.addChild(sxCourse);
gkCourse.addChild(yyCourse);
gkCourse.addChild(lzCourse);
gkCourse.info();
}
}
輸出結(jié)果為:
課程:理科高考,分數(shù):750
>課程:語文,分數(shù):150
>課程:數(shù)學(xué),分數(shù):150
>課程:英語,分數(shù):150
>課程:理綜,分數(shù):300
>課程:物理,分數(shù):110
>課程:化學(xué),分數(shù):100
>課程:生物,分數(shù):90
這里和透明方式不一樣,葉子節(jié)點不具備addChild功能,所以無法調(diào)用,而上面的示例中時可以被調(diào)用,但是調(diào)用之后顯示不支持,這就是這兩種寫法最大的區(qū)別。
組合模式角色
從上面示例中,可以看到組合模式包含了以下三個角色:
- 抽象根節(jié)點(Component):定義系統(tǒng)各層次對象的公有屬性和方法,可以預(yù)先定義一些默認行為和屬性。
- 樹枝節(jié)點(Composite):定義樹枝節(jié)點的行為,存儲子節(jié)點,組合樹枝節(jié)點和葉子節(jié)點形成一個樹形結(jié)構(gòu)。
- 葉子節(jié)點(Leaf):是系統(tǒng)遍歷層次中的最小單位,下面沒有子節(jié)點。
組合模式在JDK源碼中的體現(xiàn)
1、HashMap
HashMap中有一個putAll方法,參數(shù)是一個Map,這就是一種組合模式的體現(xiàn):

另外還有ArrayList中的addAll方法也是一樣。
2、MyBatis中有一個SqlNode接口,下面很多一級標(biāo)簽:

然后一級標(biāo)簽下面又有二級標(biāo)簽(這就是組合模式的體現(xiàn)):
組合模式應(yīng)用場景
組合模式一般應(yīng)用在有層級關(guān)系的場景,最經(jīng)典的就是樹形菜單,文件和文件夾的管理等
享元模式優(yōu)缺點
優(yōu)點:清楚的定義了分層次的復(fù)雜對象,讓客戶端可以忽略層次的差異,方便對整個層次進行動態(tài)控制。
缺點:其葉子和樹枝的聲明是實現(xiàn)類而不是接口,違反了依賴倒置原則,而且組合模式會使設(shè)計更加抽象不好理解。
總結(jié)
本文主要介紹了組合模式,并介紹了普通的聚合和組合之間的區(qū)別,并通過例子詳細解釋了組合模式中的透明寫法和安全寫法的區(qū)別,最后結(jié)合在JDK和MyBatis源碼中的運用來進一步理解組合模式的運用。
請關(guān)注我,和孤狼一起學(xué)習(xí)進步。
到此這篇關(guān)于設(shè)計模式系列之組合模式及其在JDK和MyBatis源碼中的運用詳解的文章就介紹到這了,更多相關(guān)組合模式在JDK和MyBatis源碼中的運用內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot用JdbcTemplates訪問Mysql實例代碼
本篇文章主要介紹了SpringBoot用JdbcTemplates訪問Mysql實例代碼,非常具有實用價值,需要的朋友可以參考下2017-05-05
Java實現(xiàn)Jar文件的遍歷復(fù)制與文件追加
這篇文章主要為大家詳細介紹了如何利用Java實現(xiàn)Jar文件的遍歷復(fù)制與文件追加功能,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-11-11
詳解@ConfigurationProperties如何裝載到Spring容器中
這篇文章主要為大家詳細介紹了@ConfigurationProperties該如何裝載到Spring容器中,文中的示例代碼講解詳細,需要的小伙伴可以參考一下2023-07-07
Java套接字(Socket)網(wǎng)絡(luò)編程入門
這篇文章主要介紹了Java套接字(Socket)網(wǎng)絡(luò)編程入門,Socket可以理解為是對TCP/IP協(xié)議的抽象,需要的朋友可以參考下2015-10-10
Java使用Flyway實現(xiàn)數(shù)據(jù)庫版本控制的技術(shù)指南
在現(xiàn)代應(yīng)用開發(fā)中,數(shù)據(jù)庫結(jié)構(gòu)經(jīng)常隨著業(yè)務(wù)需求不斷演變,使用手動SQL腳本管理數(shù)據(jù)庫版本,不僅容易出現(xiàn)錯誤,還難以跟蹤和回滾,Flyway是一個強大的數(shù)據(jù)庫遷移工具,能夠幫助開發(fā)者高效管理和自動化數(shù)據(jù)庫的版本控制,本文將介紹Flyway的基本功能及其在SpringBoot項目中的實踐2025-02-02
Spring Boot 中的任務(wù)執(zhí)行器基本概念及使用方法
務(wù)執(zhí)行器是 Spring Boot 中的一個非常實用的模塊,它可以簡化異步任務(wù)的開發(fā)和管理,在本文中,我們介紹了任務(wù)執(zhí)行器的基本概念和使用方法,以及一個完整的示例代碼,需要的朋友可以參考下2023-07-07

