组合模式coding
生活随笔
收集整理的這篇文章主要介紹了
组合模式coding
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
首先我們來(lái)創(chuàng)建一個(gè)組合模式的包,新建一個(gè)包名,composite,那我們現(xiàn)在引入一個(gè)業(yè)務(wù)場(chǎng)景,我們就拿一個(gè)網(wǎng)站來(lái)說(shuō),這里面有很多課程,也有課程目錄,那課程有名稱(chēng)與價(jià)格,例如說(shuō)JAVA的課程,那JAVA的課程分屬于JAVA課程這個(gè)目錄下,JAVA課程這個(gè)目錄,有很多JAVA課程,Python課程目錄有很多Python課程,如果我們使課程的目錄和課程呢,繼承同一個(gè)抽象類(lèi),例如目錄組件,那就可以把課程本身,還有由課程組合成的目錄呢,視為同一類(lèi)對(duì)象,進(jìn)行操作,但是在操作上,還會(huì)有一些差別,具體的差別定制化處理,例如說(shuō)打印這個(gè)操作,我們可以打印一個(gè)課程的名稱(chēng)和價(jià)格,我們也可以打印這個(gè)課程目錄,如果我們打印這個(gè)課程目錄,就會(huì)把這個(gè)課程目錄下邊所有的課程打印出來(lái),對(duì)于這個(gè)操作我們可以認(rèn)為,是一類(lèi)操作,對(duì)他們兩是想通的,那我們首先創(chuàng)建一個(gè)抽象的上層組件,目錄組件,CatalogComponent
看看組合模式的樹(shù)狀結(jié)構(gòu),我們把課程和課程目錄全部都繼承目錄組件,對(duì)于print方法他們兩可以一致的處理,對(duì)于其他不一樣的方法呢,在這個(gè)抽象父類(lèi)上,會(huì)有默認(rèn)的實(shí)現(xiàn),例如說(shuō)拋出異常,所以當(dāng)Course去調(diào)用add和remove的時(shí)候,就會(huì)拋出異常,同時(shí)課程目錄去getPrice的時(shí)候,也會(huì)拋出異常,因?yàn)檫@個(gè)目錄本身他沒(méi)有價(jià)格,當(dāng)然我們也可以重寫(xiě),比如這個(gè)目錄一旦獲取價(jià)格的話,就代表獲取這個(gè)目錄下邊所有課程價(jià)格的和,這樣也是可以的,還是看我們具體的業(yè)務(wù)場(chǎng)景,那在我們這里邊為了區(qū)分度,getPrice我們就認(rèn)為這個(gè)目錄本身是沒(méi)有價(jià)值的,例如你們?cè)趯W(xué)習(xí)網(wǎng)上也不可能花錢(qián),去買(mǎi)一個(gè)課程目錄來(lái),那對(duì)于name課程目錄是可以重寫(xiě)的,那我們現(xiàn)在回到這個(gè)類(lèi)里面,getName已經(jīng)加入進(jìn)來(lái)了
而這兩個(gè)的關(guān)系可以看出來(lái),是一個(gè)組合關(guān)系,這個(gè)菱形的箭頭,可以看出來(lái)這里有一個(gè)星號(hào),那這個(gè)星就是代表多的意思,例如說(shuō)items,這個(gè)是一個(gè)集合,那UML非常清晰,現(xiàn)在來(lái)寫(xiě)一下,測(cè)試類(lèi),直接創(chuàng)建一個(gè)Test
package com.learn.design.pattern.structural.composite;/*** CatalogComponent這個(gè)類(lèi)是抽象類(lèi)* 對(duì)于這個(gè)抽象類(lèi)里邊是有默認(rèn)實(shí)現(xiàn)的* 交給子類(lèi)來(lái)決定* 是否需要重寫(xiě)他* 因?yàn)檎n程目錄和課程本身* 這兩個(gè)層次的對(duì)象* 操作并不完全一樣* 但是呢又有一樣的* 所以我們直接在父類(lèi)上把這些方法的實(shí)現(xiàn)直接都寫(xiě)完* 然后交由子類(lèi)選擇性的重寫(xiě)* 那這個(gè)抽象組件就寫(xiě)好了* 接下來(lái)我們會(huì)使課程和課程目錄* 這兩個(gè)類(lèi)來(lái)繼承這個(gè)抽象類(lèi)* 現(xiàn)在我們來(lái)創(chuàng)建一個(gè)課程類(lèi)* * * @author Leon.Sun**/
public abstract class CatalogComponent {/*** 那我們寫(xiě)一個(gè)add* add操作呢肯定是add自己本身* 然后我們交由課程和課程目錄來(lái)繼承這個(gè)抽象類(lèi)* 同理我們?cè)僭黾右恍┢渌? * * @param catalogComponent*/public void add(CatalogComponent catalogComponent){/*** 這里直接寫(xiě)上不支持添加操作* * */throw new UnsupportedOperationException("不支持添加操作");}/*** 從第二個(gè)開(kāi)始remove* * * @param catalogComponent*/public void remove(CatalogComponent catalogComponent){/*** 不支持刪除操作* * */throw new UnsupportedOperationException("不支持刪除操作");}/*** 第三個(gè)就是獲取名稱(chēng)* 返回值就是String類(lèi)型* * * @param catalogComponent* @return*/public String getName(CatalogComponent catalogComponent){/*** 不支持獲取名稱(chēng)操作* * */throw new UnsupportedOperationException("不支持獲取名稱(chēng)操作");}/*** 這個(gè)方法需要改造成getPrice* 獲取具體的價(jià)格* 返回值是double* * * @param catalogComponent* @return*/public double getPrice(CatalogComponent catalogComponent){/*** 不支持獲取價(jià)格操作* * */throw new UnsupportedOperationException("不支持獲取價(jià)格操作");}/*** 下邊就是打印* 打印很好理解* 如果組件是課程* 那就打印課程* 如果組件是課程目錄* 那就打印這個(gè)課程目錄下面的課程* * 然后來(lái)到最上層的抽象類(lèi)* 也把里面的入?yún)⑷サ? 這樣就可以了* * */public void print(){/*** 不支持打印操作* * */throw new UnsupportedOperationException("不支持打印操作");}
}
package com.learn.design.pattern.structural.composite;/*** 我們使他繼承CatalogComponent* 這個(gè)課程的類(lèi)就寫(xiě)好了* 然后我們?cè)賹?xiě)一個(gè)課程的目錄類(lèi)* 我們?cè)賱?chuàng)建一個(gè)類(lèi)* CourseCatalog* * * @author Leon.Sun**/
public class Course extends CatalogComponent {/*** 他有兩個(gè)屬性* 課程的名字* */private String name;/*** 還有課程的價(jià)格* 這里面就用小double了* 我們主要關(guān)注組合模式* * */private double price;/*** 我們把構(gòu)造器寫(xiě)一下* * * @param name* @param price*/public Course(String name, double price) {this.name = name;this.price = price;}/*** 還有我們要重寫(xiě)抽象類(lèi)的方法* 重寫(xiě)哪幾個(gè)方法呢* 首先對(duì)于add* add是目錄類(lèi)的方法* add只有目錄可以add* 課程不能在他的子節(jié)點(diǎn)再添加課程了* 但是課程目錄可以* remove也是同理* 課程目錄可以保存* 可以remove一個(gè)課程* 也可以add一個(gè)課程* 但是課程本身可以獲取課程的名字* 課程的報(bào)價(jià)* 還有打印具體的課程* 所以重寫(xiě)這三個(gè)方法* * */@Overridepublic String getName(CatalogComponent catalogComponent) {/*** 這里的實(shí)現(xiàn)我們也要改一下* return什么呢* this.name* * */return this.name;}@Overridepublic double getPrice(CatalogComponent catalogComponent) {/*** 價(jià)格也是一樣* this.price* * */return this.price;}/*** 課程里的入?yún)⒁膊恍枰? 因?yàn)榇蛴〉臅r(shí)候* 打印的name和price呢* 也是課程本身的* * */@Overridepublic void print() {/*** print這里面我們也重寫(xiě)一下* * */System.out.println("Course Name:"+name+" Price:"+price);}
}
package com.learn.design.pattern.structural.composite;import java.util.ArrayList;
import java.util.List;/*** 他同樣的繼承CatalogComponent** * @author Leon.Sun**/
public class CourseCatalog extends CatalogComponent {/*** 在目錄里面肯定有很多的課程* 而這個(gè)課程又是目錄組件* 所以我們可以在這里面聲明一個(gè)* 目錄組件是個(gè)抽象類(lèi)* * */private List<CatalogComponent> items = new ArrayList<CatalogComponent>();/*** 目錄本身還有name* 比如說(shuō)這個(gè)是JAVA課程的目錄* 那這個(gè)就是他的名稱(chēng)* * */private String name;/*** 增加這么一個(gè)屬性* * */private Integer level;/*** 然后我們寫(xiě)一下他的構(gòu)造器* * 然后把level傳進(jìn)來(lái)* * * * @param name* @param level*/public CourseCatalog(String name,Integer level) {this.name = name;/*** 然后在這里面賦值* 為什么要用Integer呢* 因?yàn)槿绻胕nt的話* 那么他會(huì)有默認(rèn)值的* 所以我們使用Integer* 然后進(jìn)行空判斷* 往下走* * */this.level = level;}/*** 現(xiàn)在我們可以繼續(xù)重寫(xiě)它的方法* 對(duì)于目錄來(lái)說(shuō)add和remove* 都是有的* 同時(shí)它還可以打印* 所以我們重寫(xiě)這三個(gè)方法* 那add的實(shí)現(xiàn)也很簡(jiǎn)單* 直接用 items.add* 把catalogComponent這個(gè)放進(jìn)來(lái)* 就可以了* * */@Overridepublic void add(CatalogComponent catalogComponent) {items.add(catalogComponent);}/*** 我們把getName寫(xiě)一下* 這里面也很簡(jiǎn)答* return this.name;* 就可以了* 來(lái)到UML* getName已經(jīng)加入進(jìn)來(lái)了* */@Overridepublic String getName(CatalogComponent catalogComponent) {return this.name;}/*** remove也是同理* 直接items.remove* 把catalogComponent這個(gè)對(duì)象放進(jìn)來(lái)* * */@Overridepublic void remove(CatalogComponent catalogComponent) {items.remove(catalogComponent);}/*** 在打印的時(shí)候呢* 我們要遍歷這個(gè)目錄* 而這個(gè)目錄就是他本身* 所以這個(gè)print方法在封裝的時(shí)候* 他并不需要入?yún)? 所以這里我們可以干掉* 這個(gè)是通過(guò)一個(gè)業(yè)務(wù)場(chǎng)景來(lái)分析* 這個(gè)方法的入?yún)⒌? 那我們來(lái)到Course里邊* * 所以根據(jù)不同的業(yè)務(wù)場(chǎng)景* 我們?nèi)绻庋b組合模式* 具體的入?yún)? 還有返回值* 到底是什么類(lèi)型* 有沒(méi)有這個(gè)參數(shù)等等* 都要根據(jù)實(shí)際的業(yè)務(wù)來(lái)確定* 那print也非常的簡(jiǎn)單* 我們直接一個(gè)for循環(huán)* 就可以了* * 來(lái)到這里邊* * * */@Overridepublic void print() {/*** 直接輸出一個(gè)this.name* 課程目錄* * */System.out.println(this.name);/*** 循環(huán)items* * */for(CatalogComponent catalogComponent : items){/*** 然后我們?cè)谳敵稣n程的時(shí)候* 我們?cè)僭黾右粋€(gè)空格* * 如果this.level不等于空的話* 那這里面就調(diào)用一下for循環(huán)* 來(lái)拼接這個(gè)空格* * 為什么用this呢* 我們也可以通過(guò)instanceof這個(gè)關(guān)鍵字* 來(lái)判斷這個(gè)類(lèi)型* 是目錄類(lèi)型還是課程類(lèi)型* 如果是目錄類(lèi)型* 再打印級(jí)別的空格個(gè)數(shù)* 那樣也是可以的* 為什么這里使用this呢* 因?yàn)槭褂胻his的時(shí)候呢* 肯定是課程目錄本身來(lái)執(zhí)行* 那我們現(xiàn)在回到Test里邊* * */if(this.level != null){for(int i = 0; i < this.level; i++){/*** 也就是我們?cè)诖蛴∧夸浀臅r(shí)候* 里面多了一些空格* 這樣在排版的時(shí)候* 就很容易看清楚* 他們的從屬關(guān)系* 我們回到Test里邊* * 在這里面再打印空格就OK了* * */System.out.print(" ");}}/*** 直接調(diào)用catalogComponent.print* 這樣這個(gè)方法的實(shí)現(xiàn)就寫(xiě)好了* 接下來(lái)我們來(lái)看一下他的UML圖* 這個(gè)位置剛剛好* * */catalogComponent.print();}}
}
package com.learn.design.pattern.structural.composite;/*** * @author Leon.Sun**/
public class Test {/*** 直接寫(xiě)一個(gè)主函數(shù)* 現(xiàn)在就把課程目錄和課程* 都認(rèn)為是CatalogComponent* 就可以了* 現(xiàn)在我們把課程和課程目錄* 都認(rèn)為是CatalogComponent* 他們兩都是這個(gè)類(lèi)型* 進(jìn)行統(tǒng)一處理* * * @param args*/public static void main(String[] args) {/*** 首先我們創(chuàng)建一個(gè)CatalogComponent* linuxCourse* new一個(gè)課程* 名字"Linux課程"* 價(jià)格呢是11* * */CatalogComponent linuxCourse = new Course("Linux課程",11);/*** 再new一個(gè)* windows課程* 操作系統(tǒng)課程* 那對(duì)于操作系統(tǒng)的課程* 我們學(xué)習(xí)網(wǎng)的目錄* 創(chuàng)建好了* * * */CatalogComponent windowsCourse = new Course("Windows課程",11);/*** 然后我們?cè)賱?chuàng)建一個(gè)JAVA課程的目錄* 這回我們要?jiǎng)?chuàng)建目錄了* javaCourseCatalog* new一個(gè)Course目錄* 這里面放課程名稱(chēng)* "Java課程目錄"* 現(xiàn)在就把我們的課程* 填進(jìn)來(lái)* * JAVA課程是二級(jí)目錄* 這個(gè)時(shí)候我們來(lái)run一下* 看一下結(jié)果* 結(jié)果已經(jīng)出來(lái)了* 我們來(lái)看一下* 現(xiàn)在就非常清晰了* 主目錄是學(xué)習(xí)網(wǎng)課程主目錄* 然后下一層有三個(gè)課程* 分別是linux windows * 還有一個(gè)課程是課程目錄* 然后在JAVA課程目錄里面又有三個(gè)* JAVA課程* 這樣通過(guò)這個(gè)排版* 他們之間的父子關(guān)系* 就非常清晰了* 那組合模式coding就完成了* 組合模式使用的時(shí)候坑非常多* 我們把坑都埋到了coding實(shí)戰(zhàn)當(dāng)中* 如果我們非常順利的編寫(xiě)下來(lái)的話* 你們對(duì)于組合模式* 應(yīng)用可能不會(huì)太深* 通過(guò)這么一個(gè)踩坑的方式* 目的是希望你們?cè)谑褂媒M合模式的時(shí)候* 剛剛這些點(diǎn)一定要注意* 同時(shí)希望你們* 對(duì)這一塊的理解能夠更加深入一些* * */CatalogComponent javaCourseCatalog = new CourseCatalog("Java課程目錄",2);/*** 我們有mmall電商一期* mmallCourse1* new一個(gè)Course* 名字是"Java電商一期"* 價(jià)格呢是55* * */CatalogComponent mmallCourse1 = new Course("Java電商一期",55);/*** 然后再創(chuàng)建一個(gè)二期* mmallCourse2* 價(jià)格是66* * */CatalogComponent mmallCourse2 = new Course("Java電商二期",66);/*** 再創(chuàng)建一個(gè)設(shè)計(jì)模式* designPattern* "Java設(shè)計(jì)模式"* 價(jià)格是77* * */CatalogComponent designPattern = new Course("Java設(shè)計(jì)模式",77);/*** 目錄里面添加這三個(gè)JAVA課程* 這三個(gè)課程就添加到JAVA課程里面* 那么我們還可以創(chuàng)建一個(gè)目錄* * */javaCourseCatalog.add(mmallCourse1);javaCourseCatalog.add(mmallCourse2);javaCourseCatalog.add(designPattern);/*** 這個(gè)目錄是一個(gè)主目錄* 我們就叫imoocMainCourseCatalog* 學(xué)習(xí)網(wǎng)的課程主目錄* 我們寫(xiě)一下他的名稱(chēng)* "學(xué)習(xí)網(wǎng)課程主目錄"* * 我們?cè)跇?gòu)造這個(gè)目錄的時(shí)候* 需要傳一個(gè)級(jí)別了* 例如這個(gè)主目錄是一個(gè)一級(jí)目錄* * * */CatalogComponent imoocMainCourseCatalog = new CourseCatalog("學(xué)習(xí)網(wǎng)課程主目錄",1);/*** 然后我們向?qū)W習(xí)網(wǎng)的課程主目錄里面添加* 添加什么呢* 我們首先添加一個(gè)linux課程* */imoocMainCourseCatalog.add(linuxCourse);/*** 再添加一個(gè)windows課程* * */imoocMainCourseCatalog.add(windowsCourse);/*** 再添加一個(gè)JAVA課程的目錄* 例如說(shuō)add這個(gè)方法* 我可以填課程* 也可以填目錄* 把他兩視為一個(gè)對(duì)象* 減少這兩個(gè)對(duì)象在使用的時(shí)候差異* 這個(gè)就是組合模式的核心* 然后我們調(diào)用一下* * * */imoocMainCourseCatalog.add(javaCourseCatalog);/*** 調(diào)用一下他的打印方法* 打印學(xué)習(xí)網(wǎng)的課程主目錄* 結(jié)果已經(jīng)出來(lái)了* 這個(gè)課程下邊有l(wèi)inux課程* 有windows課程* 還有他的價(jià)格* 三個(gè)JAVA課程* 那現(xiàn)在在輸出中已經(jīng)把它們打平了* 因?yàn)槲覀冊(cè)谀夸浝镞? 并沒(méi)有寫(xiě)上具體的目錄名稱(chēng)* 所以我們現(xiàn)在來(lái)加上* 我們?cè)賠un一下* 結(jié)果已經(jīng)出來(lái)了* 我們看一下* 主目錄加了兩個(gè)空格* 在主目錄下面有兩個(gè)操作系統(tǒng)的課程* 然后是JAVA課程目錄* Java課程目錄和他兩是平級(jí)的* 然后在JAVA課程目錄下邊* 又有三個(gè)JAVA課程* 那我們看一下* 他的缺點(diǎn)是什么呢* 也就是如果我們要?jiǎng)討B(tài)的* 對(duì)類(lèi)型進(jìn)行限制的話* 會(huì)使這個(gè)業(yè)務(wù)邏輯變得復(fù)雜* 我們現(xiàn)在想在JAVA目錄本身是一個(gè)二級(jí)目錄* 因?yàn)閷W(xué)習(xí)網(wǎng)主目錄是一級(jí)目錄* 他呢是二級(jí)目錄* 對(duì)這個(gè)空格打印的時(shí)候* 需要打印兩個(gè)空格* 從而在打印上來(lái)區(qū)分* 從屬關(guān)系* 那在這里就需要進(jìn)行動(dòng)態(tài)的判斷了* 所以這個(gè)坑也是埋了一下* 為了讓你們能體會(huì)到* 組合模式的缺點(diǎn)* 所以我們這代碼還要改* 我們現(xiàn)在想一下* 剛才我們都改什么了* 首先對(duì)于組合模式* 最上層的抽象組件* 參數(shù)和返回值* 這里要多花心思* 來(lái)確定他* 還有目錄在重寫(xiě)方法的時(shí)候* 來(lái)確定哪些組合業(yè)務(wù)邏輯的重寫(xiě)* 哪些是不符合業(yè)務(wù)邏輯的* 例如目錄本身并沒(méi)有價(jià)格* 那如果我們重寫(xiě)為這個(gè)目錄獲取價(jià)格的時(shí)候* 代表獲取這個(gè)目錄下邊所有課程的價(jià)格* 那我們還要考慮* 一旦這個(gè)目錄下邊還有目錄的話* 是不是要做深層次的遞歸* 那這些都是我們要考慮的點(diǎn)* 就例如我們現(xiàn)在碰到的問(wèn)題* 我們需要排版更清晰的* 從屬關(guān)系* 那我們還要在目錄這個(gè)結(jié)構(gòu)下* 增加一個(gè)level* 這么一個(gè)屬性* 那很簡(jiǎn)單* 來(lái)增加一個(gè)屬性* * * * */imoocMainCourseCatalog.print();}
}
?
總結(jié)
以上是生活随笔為你收集整理的组合模式coding的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 组合模式讲解
- 下一篇: 组合模式源码解析(jdk+mybatis