组合模式——透明组合模式,安全组合模式
組合模式
概述
葉子節(jié)點(diǎn)進(jìn)行相關(guān)的操作。
可以將這顆樹(shù)理解成一個(gè)大的容器,容器里面包含很多的成員對(duì)象,這些成員對(duì)象即可是容器對(duì)象也可以是葉子對(duì)象。
但是由于容器對(duì)象和葉子對(duì)象在功能上面的區(qū)別,使得我們?cè)谑褂玫倪^(guò)程中必須要區(qū)分容器對(duì)象和葉子對(duì)象,但是這樣就會(huì)給客戶帶來(lái)不必要的麻煩,
作為客戶而已,它始終希望能夠一致的對(duì)待容器對(duì)象和葉子對(duì)象。
定義:
又名部分整體模式,是用于把一組相似的對(duì)象當(dāng)作一個(gè)單一的對(duì)象。
組合模式依據(jù)樹(shù)形結(jié)構(gòu)來(lái)組合對(duì)象,用來(lái)表示部分以及整體層次。
這種類型的設(shè)計(jì)模式屬于結(jié)構(gòu)型模式,它創(chuàng)建了對(duì)象組的樹(shù)形結(jié)構(gòu)。
?
5.6.2 結(jié)構(gòu)
組合模式主要包含三種角色:
- 抽象根節(jié)點(diǎn)(Component):定義系統(tǒng)各層次對(duì)象的共有方法和屬性,可以預(yù)先定義一些默認(rèn)行為和屬性。
- 樹(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)。
- 葉子節(jié)點(diǎn)(Leaf):葉子節(jié)點(diǎn)對(duì)象,其下再無(wú)分支,是系統(tǒng)層次遍歷的最小單位。
?
5.6.3 案例實(shí)現(xiàn)
【例】軟件菜單
如下圖,我們?cè)谠L問(wèn)別的一些管理系統(tǒng)時(shí),經(jīng)常可以看到類似的菜單。
一個(gè)菜單可以包含菜單項(xiàng)(菜單項(xiàng)是指不再包含其他內(nèi)容的菜單條目),也可以包含帶有其他菜單項(xiàng)的菜單,
因此使用組合模式描述菜單就很恰當(dāng),我們的需求是針對(duì)一個(gè)菜單,打印出其包含的所有菜單以及菜單項(xiàng)的名稱。
要實(shí)現(xiàn)該案例,我們先畫(huà)出類圖:
代碼實(shí)現(xiàn):
不管是菜單還是菜單項(xiàng),都應(yīng)該繼承自統(tǒng)一的接口,這里姑且將這個(gè)統(tǒng)一的接口稱為菜單組件。
MenuComponent.java
package com.itheima.pattern.combination;/*** @version v1.0* @ClassName: MenuComponent* @Description: 菜單組件 : 屬于抽象根節(jié)點(diǎn)* @Author: dym*/ public abstract class MenuComponent {//菜單組件的名稱protected String name;//菜單組件的層級(jí)protected int level;//添加子菜單public void add(MenuComponent menuComponent) {throw new UnsupportedOperationException();}//移除子菜單public void remove(MenuComponent menuComponent) {throw new UnsupportedOperationException();}//獲取指定的子菜單public MenuComponent getChild(int index) {throw new UnsupportedOperationException();}//獲取菜單或者菜單項(xiàng)的名稱public String getName() {return name;}//打印菜單名稱的方法(包含子菜單和字菜單項(xiàng))public abstract void print(); }MenuItem.java
package com.itheima.pattern.combination;import com.itheima.principles.demo1.SougouInput;/*** @version v1.0* @ClassName: MenuItem* @Description: 菜單項(xiàng)類 : 屬于葉子節(jié)點(diǎn)* @Author: dym*/ public class MenuItem extends MenuComponent {public MenuItem(String name,int level) {this.name = name;this.level = level;}public void print() {//打印菜單項(xiàng)的名稱for(int i = 0; i < level; i++) {System.out.print("--");}System.out.println(name);} }Menu.java
package com.itheima.pattern.combination;import java.util.ArrayList; import java.util.List;/*** @version v1.0* @ClassName: Menu* @Description: 菜單類 : 屬于樹(shù)枝節(jié)點(diǎn)* @Author: dym*/ public class Menu extends MenuComponent {//菜單可以有多個(gè)子菜單或者子菜單項(xiàng)private List<MenuComponent> menuComponentList = new ArrayList<MenuComponent>();//構(gòu)造方法public Menu(String name,int level) {this.name = name;this.level = level;}@Overridepublic void add(MenuComponent menuComponent) {menuComponentList.add(menuComponent);}@Overridepublic void remove(MenuComponent menuComponent) {menuComponentList.remove(menuComponent);}@Overridepublic MenuComponent getChild(int index) {return menuComponentList.get(index);}@Overridepublic void print() {//打印菜單名稱for(int i = 0; i < level; i++) {System.out.print("--");}System.out.println(name);//打印子菜單或者子菜單項(xiàng)名稱for (MenuComponent component : menuComponentList) {component.print();}} }Client.java
package com.itheima.pattern.combination;/*** @version v1.0* @ClassName: Client* @Description: TODO(一句話描述該類的功能)* @Author: dym*/ public class Client {public static void main(String[] args) {//創(chuàng)建菜單樹(shù)MenuComponent menu1 = new Menu("菜單管理",2);menu1.add(new MenuItem("頁(yè)面訪問(wèn)",3));menu1.add(new MenuItem("展開(kāi)菜單",3));menu1.add(new MenuItem("編輯菜單",3));menu1.add(new MenuItem("刪除菜單",3));menu1.add(new MenuItem("新增菜單",3));MenuComponent menu2 = new Menu("權(quán)限管理",2);menu2.add(new MenuItem("頁(yè)面訪問(wèn)",3));menu2.add(new MenuItem("提交保存",3));MenuComponent menu3 = new Menu("角色管理",2);menu3.add(new MenuItem("頁(yè)面訪問(wèn)",3));menu3.add(new MenuItem("新增角色",3));menu3.add(new MenuItem("修改角色",3));//創(chuàng)建一級(jí)菜單MenuComponent component = new Menu("系統(tǒng)管理",1);//將二級(jí)菜單添加到一級(jí)菜單中component.add(menu1);component.add(menu2);component.add(menu3);//打印菜單名稱(如果有子菜單一塊打印)component.print();} }這里的MenuComponent定義為抽象類,
因?yàn)橛幸恍┕灿械膶傩院托袨橐谠擃愔袑?shí)現(xiàn),Menu和MenuItem類就可以只覆蓋自己感興趣的方法,
而不用搭理不需要或者不感興趣的方法,
舉例來(lái)說(shuō),
Menu類可以包含子菜單,因此需要覆蓋add()、remove()、getChild()方法,
但是MenuItem就不應(yīng)該有這些方法。
這里給出的默認(rèn)實(shí)現(xiàn)是拋出異常,你也可以根據(jù)自己的需要改寫(xiě)默認(rèn)實(shí)現(xiàn)
Menu類已經(jīng)實(shí)現(xiàn)了除了getName方法的其他所有方法,因?yàn)镸enu類具有添加菜單,移除菜單和獲取子菜單的功能。
MenuItem是菜單項(xiàng),不能再有子菜單,所以添加菜單,移除菜單和獲取子菜單的功能并不能實(shí)現(xiàn)。
組合模式的分類
在使用組合模式時(shí),根據(jù)抽象構(gòu)件類的定義形式,我們可將組合模式分為透明組合模式和安全組合模式兩種形式。
-
透明組合模式
透明組合模式中,抽象根節(jié)點(diǎn)角色中聲明了所有用于管理成員對(duì)象的方法,比如在示例中?MenuComponent?聲明了?add、remove?、getChild?方法,這樣做的好處是確保所有的構(gòu)件類都有相同的接口。透明組合模式也是組合模式的標(biāo)準(zhǔn)形式。
透明組合模式的缺點(diǎn)是不夠安全,因?yàn)槿~子對(duì)象和容器對(duì)象在本質(zhì)上是有區(qū)別的,葉子對(duì)象不可能有下一個(gè)層次的對(duì)象,即不可能包含成員對(duì)象,因此為其提供 add()、remove() 等方法是沒(méi)有意義的,這在編譯階段不會(huì)出錯(cuò),但在運(yùn)行階段如果調(diào)用這些方法可能會(huì)出錯(cuò)(如果沒(méi)有提供相應(yīng)的錯(cuò)誤處理代碼)
-
安全組合模式
在安全組合模式中,在抽象構(gòu)件角色中沒(méi)有聲明任何用于管理成員對(duì)象的方法,而是在樹(shù)枝節(jié)點(diǎn)?Menu?類中聲明并實(shí)現(xiàn)這些方法。安全組合模式的缺點(diǎn)是不夠透明,因?yàn)槿~子構(gòu)件和容器構(gòu)件具有不同的方法,且容器構(gòu)件中那些用于管理成員對(duì)象的方法沒(méi)有在抽象構(gòu)件類中定義,因此客戶端不能完全針對(duì)抽象編程,必須有區(qū)別地對(duì)待葉子構(gòu)件和容器構(gòu)件。
優(yōu)點(diǎn)
- 組合模式可以清楚地定義分層次的復(fù)雜對(duì)象,表示對(duì)象的全部或部分層次,它讓客戶端忽略了層次的差異,方便對(duì)整個(gè)層次結(jié)構(gòu)進(jìn)行控制。
- 客戶端可以一致地使用一個(gè)組合結(jié)構(gòu)或其中單個(gè)對(duì)象,不必關(guān)心處理的是單個(gè)對(duì)象還是整個(gè)組合結(jié)構(gòu),簡(jiǎn)化了客戶端代碼。
- 在組合模式中增加新的樹(shù)枝節(jié)點(diǎn)和葉子節(jié)點(diǎn)都很方便,無(wú)須對(duì)現(xiàn)有類庫(kù)進(jìn)行任何修改,符合“開(kāi)閉原則”。
- 組合模式為樹(shù)形結(jié)構(gòu)的面向?qū)ο髮?shí)現(xiàn)提供了一種靈活的解決方案,通過(guò)葉子節(jié)點(diǎn)和樹(shù)枝節(jié)點(diǎn)的遞歸組合,可以形成復(fù)雜的樹(shù)形結(jié)構(gòu),但對(duì)樹(shù)形結(jié)構(gòu)的控制卻非常簡(jiǎn)單。
?
使用場(chǎng)景
組合模式正是應(yīng)樹(shù)形結(jié)構(gòu)而生,所以組合模式的使用場(chǎng)景就是出現(xiàn)樹(shù)形結(jié)構(gòu)的地方。
比如:文件目錄顯示,多級(jí)目錄呈現(xiàn)等樹(shù)形結(jié)構(gòu)數(shù)據(jù)的操作
?
總結(jié)
以上是生活随笔為你收集整理的组合模式——透明组合模式,安全组合模式的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 源码解析 使用tomcat作为web容器
- 下一篇: JDK源码解析 Integer类使用了享