Java結(jié)構(gòu)型設(shè)計(jì)模式之組合模式詳解
組合模式
組合模式(Composite Pattern)也稱為整體-部分(Part-Whole)模式,屬于結(jié)構(gòu)型模式。
它的宗旨是通過(guò)將單個(gè)對(duì)象(葉子節(jié)點(diǎn))和組合對(duì)象(樹(shù)枝節(jié)點(diǎn))用相同的接口進(jìn)行表示,使得客戶端對(duì)單個(gè)對(duì)象和組合對(duì)象的使用具有一致性。
組合模式一般用來(lái)描述整體與部分的關(guān)系,它將對(duì)象組織到樹(shù)形結(jié)構(gòu)中,最頂層的節(jié)點(diǎn)稱為根節(jié)點(diǎn),根節(jié)點(diǎn)下面可以包含樹(shù)枝節(jié)點(diǎn)和葉子節(jié)點(diǎn),樹(shù)枝節(jié)點(diǎn)下面又可以包含樹(shù)枝節(jié)點(diǎn)和葉子節(jié)點(diǎn)。
應(yīng)用場(chǎng)景
1.希望客戶端可以忽略組合對(duì)象與單個(gè)對(duì)象的差異時(shí)。
2.對(duì)象層次具備整體和部分,呈樹(shù)形結(jié)構(gòu)。
例如:樹(shù)形菜單,文件、文件夾的管理。
優(yōu)缺點(diǎn)
優(yōu)點(diǎn):
1、高層模塊調(diào)用簡(jiǎn)單。
2、節(jié)點(diǎn)自由增加。
缺點(diǎn):
1.在使用組合模式時(shí),其葉子和樹(shù)枝的聲明都是實(shí)現(xiàn)類,而不是接口,違反了依賴倒置原則。
主要角色
組合模式主要包含3個(gè)角色:
1.抽象根節(jié)點(diǎn)(Component)
定義系統(tǒng)各層次對(duì)象的共有方法和屬性,可以預(yù)先定義一些默認(rèn)行為和屬性。
2.樹(shù)枝節(jié)點(diǎn)(Composite)
定義樹(shù)枝節(jié)點(diǎn)的行為,存儲(chǔ)子節(jié)點(diǎn),組合樹(shù)枝節(jié)點(diǎn)和葉子節(jié)點(diǎn)形成一個(gè)樹(shù)形結(jié)構(gòu)。
3.葉子節(jié)點(diǎn)(Laf)
葉子節(jié)點(diǎn)對(duì)象,其下再無(wú)分支,是系統(tǒng)層次遍歷的最小單位。
組合模式結(jié)構(gòu)
分類
組合模式在具體實(shí)現(xiàn)上,有兩種不同的方式,分別是透明組合模式和安全組合模式。
透明組合模式將公共接口封裝到抽象根節(jié)點(diǎn)(Component)中,系統(tǒng)所有節(jié)點(diǎn)具備一致行為,如果當(dāng)系統(tǒng)絕大多數(shù)層次具備相同的公共行為時(shí),采用透明組合模式會(huì)更好。但是為剩下少數(shù)層次節(jié)點(diǎn)引入不需要的方法。
如果當(dāng)系統(tǒng)各個(gè)層次差異性行為較多或者樹(shù)節(jié)點(diǎn)層次相對(duì)穩(wěn)定時(shí),則采用安全組合模式。
透明組合模式
透明組合模式是把所有公共方法都定義在抽象根節(jié)點(diǎn)中,這樣做的好處是客戶端無(wú)需分辨是葉子節(jié)點(diǎn)(Leaf)和樹(shù)枝節(jié)點(diǎn)(Composite),它們具備完全一致的接口。缺點(diǎn)是葉子節(jié)點(diǎn)(Leaf)會(huì)繼承得到一些它所不需要(管理子類操作的方法)的方法,這與設(shè)計(jì)模式接口隔離原則相違背。
創(chuàng)建抽象根節(jié)點(diǎn)
把所有可能用到的方法都定義到這個(gè)最頂層的抽象類中,但是不寫任何邏輯處理的代碼,而是直接拋出異常。
禁止使用抽象方法,否則子類必須實(shí)現(xiàn),于是體現(xiàn)不出各個(gè)子類的差異。子類只需要重寫有差異的方法進(jìn)行覆蓋即可。
舉例:分類目錄為根節(jié)點(diǎn),具體分類為樹(shù)枝節(jié)點(diǎn),分類下的商品為葉子節(jié)點(diǎn)。
public abstract class Component { public String getName(Component component) { throw new UnsupportedOperationException("getName is not supported"); } public double getPrice(Component component) { throw new UnsupportedOperationException("getPrice is not supported"); } public String print() { throw new UnsupportedOperationException("print is not supported"); } public boolean addChild(Component component) { throw new UnsupportedOperationException("addChild is not supported"); } public boolean removeChild(Component component) { throw new UnsupportedOperationException("removeChild is not supported"); } public Component getChild(int index) { throw new UnsupportedOperationException("getChild is not supported"); } }
創(chuàng)建樹(shù)枝節(jié)點(diǎn)
public class CompositeCategory extends Component { private String name; private List<Component> componentList = new ArrayList<Component>(); public CompositeCategory(String name) { this.name = name; } @Override public String print() { StringBuilder builder = new StringBuilder(this.name); for (Component component : this.componentList) { if (component instanceof CompositeCategory) { builder.append("\n" + "+-" + component.print()); } else { builder.append("\n" + "+--" + component.print()); } } return builder.toString(); } @Override public boolean addChild(Component component) { return this.componentList.add(component); } @Override public boolean removeChild(Component component) { return this.componentList.remove(component); } @Override public Component getChild(int index) { return this.componentList.get(index); } }
創(chuàng)建葉子節(jié)點(diǎn)
public class CompositeProduct extends Component { private String name; private Double price; public CompositeProduct(String name, Double price) { this.name = name; this.price = price; } @Override public String print() { return this.name + " (¥" + this.price + "元)"; } @Override public String getName(Component component) { return this.name; } @Override public double getPrice(Component component) { return this.price; } }
客戶端調(diào)用
public static void main(String[] args) { // 根節(jié)點(diǎn) Component root = new CompositeCategory("分類目錄"); // 樹(shù)枝節(jié)點(diǎn) Component categoryA = new CompositeCategory("分類A"); Component categoryB = new CompositeCategory("分類B"); // 葉子節(jié)點(diǎn) Component productA = new CompositeProduct("productA ", 20.5); Component productB = new CompositeProduct("productB ", 30.5); Component productC = new CompositeProduct("productC", 25.5); root.addChild(categoryA); categoryA.addChild(productA); root.addChild(categoryB); categoryB.addChild(productB); categoryB.addChild(productC); System.out.println(root.print()); System.out.println("-----------------------"); Component child = root.getChild(1); System.out.println(child.print()); System.out.println("-----------------------"); root.removeChild(categoryA); System.out.println(root.print()); }
分類目錄
+-分類A
+--productA (¥20.5元)
+-分類B
+--productB (¥30.5元)
+--productC (¥25.5元)
-----------------------
分類B
+--productB (¥30.5元)
+--productC (¥25.5元)
-----------------------
分類目錄
+-分類B
+--productB (¥30.5元)
+--productC (¥25.5元)
安全組合模式
安全組合模式是只規(guī)定系統(tǒng)各個(gè)層次的最基礎(chǔ)的一致行為,而把組合(樹(shù)節(jié)點(diǎn))本身的方法(管理子類對(duì)象的添加,刪除等)放到自身當(dāng)中。
安全組合模式的好處是接口定義職責(zé)清晰,符合設(shè)計(jì)模式單一職責(zé)原側(cè)和接口隔離原則;缺點(diǎn)是客戶需要區(qū)分樹(shù)枝節(jié)點(diǎn)(Composite)和葉子節(jié)點(diǎn)(Leaf),這樣才能正確處理各個(gè)層次的操作,客戶端無(wú)法依賴抽象(Component),違背了設(shè)計(jì)模式依賴倒置原則。
創(chuàng)建抽象根節(jié)點(diǎn)
public abstract class Component { protected String name; public Component(String name) { this.name = name; } public abstract String print(); }
創(chuàng)建樹(shù)枝節(jié)點(diǎn)
public class CompositeCategory extends Component { private List<Component> componentList; public CompositeCategory(String name) { super(name); this.componentList = new ArrayList<Component>(); } @Override public String print() { StringBuilder builder = new StringBuilder(this.name); for (Component component : this.componentList) { if (component instanceof CompositeCategory) { builder.append("\n" + "+-" + component.print()); } else { builder.append("\n" + "+--" + component.print()); } } return builder.toString(); } public boolean addChild(Component component) { return this.componentList.add(component); } public boolean removeChild(Component component) { return this.componentList.remove(component); } public Component getChild(int index) { return this.componentList.get(index); } }
創(chuàng)建葉子節(jié)點(diǎn)
public class CompositeProduct extends Component { private Double price; public CompositeProduct(String name, Double price) { super(name); this.price = price; } @Override public String print() { return this.name + " (¥" + this.price + "元)"; } public String getName() { return this.name; } public double getPrice() { return this.price; } }
客戶端調(diào)用
public static void main(String[] args) { // 根節(jié)點(diǎn) CompositeCategory root = new CompositeCategory("分類目錄"); // 樹(shù)枝節(jié)點(diǎn) CompositeCategory categoryA = new CompositeCategory("分類A"); CompositeCategory categoryB = new CompositeCategory("分類B"); // 葉子節(jié)點(diǎn) CompositeProduct productA = new CompositeProduct("productA", 20.5); CompositeProduct productB = new CompositeProduct("productB", 30.5); CompositeProduct productC = new CompositeProduct("productC", 25.5); root.addChild(categoryA); categoryA.addChild(productA); root.addChild(categoryB); categoryB.addChild(productB); categoryB.addChild(productC); System.out.println(root.print()); System.out.println("-----------------------"); Component child = root.getChild(1); System.out.println(child.print()); System.out.println("-----------------------"); root.removeChild(categoryA); System.out.println(root.print()); }
分類目錄
+-分類A
+--productA (¥20.5元)
+-分類B
+--productB (¥30.5元)
+--productC (¥25.5元)
-----------------------
分類B
+--productB (¥30.5元)
+--productC (¥25.5元)
-----------------------
分類目錄
+-分類B
+--productB (¥30.5元)
+--productC (¥25.5元)
到此這篇關(guān)于Java結(jié)構(gòu)型設(shè)計(jì)模式之組合模式詳解的文章就介紹到這了,更多相關(guān)Java組合模式內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
springboot實(shí)現(xiàn)分頁(yè)功能的完整代碼
Spring Boot是一個(gè)快速開(kāi)發(fā)框架,它提供了很多便捷的功能,其中包括分頁(yè)查詢,下面這篇文章主要給大家介紹了關(guān)于springboot實(shí)現(xiàn)分頁(yè)功能的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-04-04java.net.ConnectException: Connection refused問(wèn)題解決辦法
這篇文章主要介紹了java.net.ConnectException: Connection refused問(wèn)題解決辦法的相關(guān)資料,需要的朋友可以參考下2016-12-12idea打開(kāi)項(xiàng)目后無(wú)法顯示目錄結(jié)構(gòu),只能顯示.iml文件問(wèn)題
這篇文章主要介紹了idea打開(kāi)項(xiàng)目后無(wú)法顯示目錄結(jié)構(gòu),只能顯示.iml文件問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-08-08Exception in thread main java.lang.NoClassDefFoundError錯(cuò)誤解決方
這篇文章主要介紹了Exception in thread main java.lang.NoClassDefFoundError錯(cuò)誤解決方法,需要的朋友可以參考下2016-08-08基于springboot微信公眾號(hào)開(kāi)發(fā)(微信自動(dòng)回復(fù))
這篇文章主要介紹了基于springboot微信公眾號(hào)開(kāi)發(fā)(微信自動(dòng)回復(fù)),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-11-11java開(kāi)發(fā)之讀寫txt文件操作的實(shí)現(xiàn)
本篇文章介紹了,java開(kāi)發(fā)之讀寫txt文件操作的實(shí)現(xiàn)。需要的朋友參考下2013-05-05簡(jiǎn)單了解spring bean作用域?qū)傩詓ingleton和prototype的區(qū)別
這篇文章主要介紹了簡(jiǎn)單了解spring bean作用域?qū)傩詓ingleton和prototype的區(qū)別,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-12-12SpringBoot如何配置數(shù)據(jù)庫(kù)主從shardingsphere
這篇文章主要介紹了SpringBoot如何配置數(shù)據(jù)庫(kù)主從shardingsphere問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-04-04