設(shè)計模式系列之組合模式及其在JDK和MyBatis源碼中的運用詳解
組合模式及其在JDK源碼中的運用 前言組合和聚合什么是組合模式示例透明組合模式透明組合模式的缺陷安全組合模式 組合模式角色組合模式在JDK源碼中的體現(xiàn)組合模式應(yīng)用場景享元模式優(yōu)缺點總結(jié)
前言
本文主要會講述組合模式的用法,并會結(jié)合在JDK和MyBatis源碼中的運用來進一步理解組合模式。
在編碼原則中,有一條是:多用組合,少用繼承。當然這里的組合和我們今天要講的組合模式并不等價,這里的組合其實就是一種聚合,那么聚合和組合有什么區(qū)別呢?
組合和聚合
人在一起叫團伙,心在一起叫團隊。用這句話來詮釋組合與聚合的區(qū)別是相對恰當?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),這樣做的好處是客戶端無需分辨當前對象是屬于樹枝節(jié)點還是葉子節(jié)點,因為它們具備了完全一致的接口,不過缺點就是葉子節(jié)點得到到了一些不屬于它的方法,比如上面的addChild方法,這違背了接口隔離性原則。
安全組合模式
安全組合模式只是規(guī)定了系統(tǒng)各個層次的最基礎(chǔ)的一致性行為,而把組合(樹節(jié)點)本身的方法(如樹枝節(jié)點管理子類的addChild等方法)放到自身當中。
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é)點(這個類當中定義了一個樹枝特有的方法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接口,下面很多一級標簽:
然后一級標簽下面又有二級標簽(這就是組合模式的體現(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é)習進步。
到此這篇關(guān)于設(shè)計模式系列之組合模式及其在JDK和MyBatis源碼中的運用詳解的文章就介紹到這了,更多相關(guān)組合模式在JDK和MyBatis源碼中的運用內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Mybatis-Plus批量添加或修改數(shù)據(jù)的3種方式總結(jié)
使用Mybatis-plus可以很方便的實現(xiàn)批量新增和批量修改,不僅比自己寫foreach遍歷方便很多,而且性能也更加優(yōu)秀,下面這篇文章主要給大家介紹了關(guān)于Mybatis-Plus批量添加或修改數(shù)據(jù)的3種方式,需要的朋友可以參考下2023-05-05Java.lang.Long.parseLong()方法詳解及示例
這個java.lang.Long.parseLong(String s) 方法解析字符串參數(shù)s作為有符號十進制長,下面這篇文章主要給大家介紹了關(guān)于Java.lang.Long.parseLong()方法詳解及示例的相關(guān)資料,需要的朋友可以參考下2023-01-01Spring Security 中如何讓上級擁有下級的所有權(quán)限(案例分析)
這篇文章主要介紹了Spring Security 中如何讓上級擁有下級的所有權(quán)限,本文通過案例分析給大家介紹的非常詳細,對大家的學(xué)習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-09-09關(guān)于Idea創(chuàng)建Java項目并引入lombok包的問題(lombok.jar包免費下載)
很多朋友遇到當idea創(chuàng)建java項目時,命名安裝了lombok插件卻不能使用注解,原因有兩個大家可以參考下本文,本文對每種原因分析給出了解決方案,需要的朋友參考下吧2021-06-06