日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > asp.net >内容正文

asp.net

1、【设计模式】组合模式

發(fā)布時間:2024/10/12 asp.net 112 豆豆
生活随笔 收集整理的這篇文章主要介紹了 1、【设计模式】组合模式 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

java設(shè)計模式之組合模式

【學(xué)習(xí)難度:★★★☆☆,使用頻率:★★★★☆】?

樹形結(jié)構(gòu)在軟件中隨處可見,例如操作系統(tǒng)中的目錄結(jié)構(gòu)、應(yīng)用軟件中的菜單、辦公系統(tǒng)中的公司組織結(jié)構(gòu)等等,如何運用面向?qū)ο蟮姆绞絹硖幚磉@種樹形結(jié)構(gòu)是組合模式需要解決的問題,組合模式通過一種巧妙的設(shè)計方案使得用戶可以一致性地處理整個樹形結(jié)構(gòu)或者樹形結(jié)構(gòu)的一部分,也可以一致性地處理樹形結(jié)構(gòu)中的葉子節(jié)點(不包含子節(jié)點的節(jié)點)和容器節(jié)點(包含子節(jié)點的節(jié)點)。下面將學(xué)習(xí)這種用于處理樹形結(jié)構(gòu)的組合模式。

?

11.1 設(shè)計殺毒軟件的框架結(jié)構(gòu)

?

????? Sunny軟件公司欲開發(fā)一個殺毒(AntiVirus)軟件,該軟件既可以對某個文件夾(Folder)殺毒,也可以對某個指定的文件(File)進(jìn)行殺毒。該殺毒軟件還可以根據(jù)各類文件的特點,為不同類型的文件提供不同的殺毒方式,例如圖像文件(ImageFile)和文本文件(TextFile)的殺毒方式就有所差異。現(xiàn)需要提供該殺毒軟件的整體框架設(shè)計方案。

?

?

????? 在介紹Sunny公司開發(fā)人員提出的初始解決方案之前,我們先來分析一下操作系統(tǒng)中的文件目錄結(jié)構(gòu),例如在Windows操作系統(tǒng)中,存在如圖11-1所示目錄結(jié)構(gòu):

圖11-1 Windows目錄結(jié)構(gòu)

????? 圖11-1可以簡化為如圖11-2所示樹形目錄結(jié)構(gòu):

圖11-2?樹形目錄結(jié)構(gòu)示意圖

????? 我們可以看出,在圖11-2中包含文件(灰色節(jié)點)和文件夾(白色節(jié)點)兩類不同的元素,其中在文件夾中可以包含文件,還可以繼續(xù)包含子文件夾,但是在文件中不能再包含子文件或者子文件夾。在此,我們可以稱文件夾為容器(Container),而不同類型的各種文件是其成員,也稱為葉子(Leaf),一個文件夾也可以作為另一個更大的文件夾的成員。如果我們現(xiàn)在要對某一個文件夾進(jìn)行操作,如查找文件,那么需要對指定的文件夾進(jìn)行遍歷,如果存在子文件夾則打開其子文件夾繼續(xù)遍歷,如果是文件則判斷之后返回查找結(jié)果。

????? Sunny軟件公司的開發(fā)人員通過分析,決定使用面向?qū)ο蟮姆绞絹韺崿F(xiàn)對文件和文件夾的操作,定義了如下圖像文件類ImageFile、文本文件類TextFile和文件夾類Folder:

//為了突出核心框架代碼,我們對殺毒過程的實現(xiàn)進(jìn)行了大量簡化 import java.util.*; //圖像文件類 class ImageFile { private String name; public ImageFile(String name) { this.name = name; } public void killVirus() { //簡化代碼,模擬殺毒 System.out.println("----對圖像文件'" + name + "'進(jìn)行殺毒"); } } //文本文件類 class TextFile { private String name; public TextFile(String name) { this.name = name; } public void killVirus() { //簡化代碼,模擬殺毒 System.out.println("----對文本文件'" + name + "'進(jìn)行殺毒"); } } //文件夾類 class Folder { private String name; //定義集合folderList,用于存儲Folder類型的成員 private ArrayList<Folder> folderList = new ArrayList<Folder>(); //定義集合imageList,用于存儲ImageFile類型的成員 private ArrayList<ImageFile> imageList = new ArrayList<ImageFile>(); //定義集合textList,用于存儲TextFile類型的成員 private ArrayList<TextFile> textList = new ArrayList<TextFile>(); public Folder(String name) { this.name = name; } //增加新的Folder類型的成員 public void addFolder(Folder f) { folderList.add(f); } //增加新的ImageFile類型的成員 public void addImageFile(ImageFile image) { imageList.add(image); } //增加新的TextFile類型的成員 public void addTextFile(TextFile text) { textList.add(text); } //需提供三個不同的方法removeFolder()、removeImageFile()和removeTextFile()來刪除成員,代碼省略 //需提供三個不同的方法getChildFolder(int i)、getChildImageFile(int i)和getChildTextFile(int i)來獲取成員,代碼省略 public void killVirus() { System.out.println("****對文件夾'" + name + "'進(jìn)行殺毒"); //模擬殺毒 //如果是Folder類型的成員,遞歸調(diào)用Folder的killVirus()方法 for(Object obj : folderList) { ((Folder)obj).killVirus(); } //如果是ImageFile類型的成員,調(diào)用ImageFile的killVirus()方法 for(Object obj : imageList) { ((ImageFile)obj).killVirus(); } //如果是TextFile類型的成員,調(diào)用TextFile的killVirus()方法 for(Object obj : textList) { ((TextFile)obj).killVirus(); } } }

編寫如下客戶端測試代碼進(jìn)行測試:

class Client { public static void main(String args[]) { Folder folder1,folder2,folder3; folder1 = new Folder("Sunny的資料"); folder2 = new Folder("圖像文件"); folder3 = new Folder("文本文件"); ImageFile image1,image2; image1 = new ImageFile("小龍女.jpg"); image2 = new ImageFile("張無忌.gif"); TextFile text1,text2; text1 = new TextFile("九陰真經(jīng).txt"); text2 = new TextFile("葵花寶典.doc"); folder2.addImageFile(image1); folder2.addImageFile(image2); folder3.addTextFile(text1); folder3.addTextFile(text2); folder1.addFolder(folder2); folder1.addFolder(folder3); folder1.killVirus(); } }

編譯并運行程序,輸出結(jié)果如下:

?

****對文件夾'Sunny的資料'進(jìn)行殺毒

****對文件夾'圖像文件'進(jìn)行殺毒

----對圖像文件'小龍女.jpg'進(jìn)行殺毒

----對圖像文件'張無忌.gif'進(jìn)行殺毒

****對文件夾'文本文件'進(jìn)行殺毒

----對文本文件'九陰真經(jīng).txt'進(jìn)行殺毒

----對文本文件'葵花寶典.doc'進(jìn)行殺毒

?

?

????? Sunny公司開發(fā)人員“成功”實現(xiàn)了殺毒軟件的框架設(shè)計,但通過仔細(xì)分析,發(fā)現(xiàn)該設(shè)計方案存在如下問題:

????? (1)?文件夾類Folder的設(shè)計和實現(xiàn)都非常復(fù)雜,需要定義多個集合存儲不同類型的成員,而且需要針對不同的成員提供增加、刪除和獲取等管理和訪問成員的方法,存在大量的冗余代碼,系統(tǒng)維護(hù)較為困難;

????? (2)?由于系統(tǒng)沒有提供抽象層,客戶端代碼必須有區(qū)別地對待充當(dāng)容器的文件夾Folder和充當(dāng)葉子的ImageFile和TextFile,無法統(tǒng)一對它們進(jìn)行處理;

????? (3)?系統(tǒng)的靈活性和可擴(kuò)展性差,如果需要增加新的類型的葉子和容器都需要對原有代碼進(jìn)行修改,例如如果需要在系統(tǒng)中增加一種新類型的視頻文件VideoFile,則必須修改Folder類的源代碼,否則無法在文件夾中添加視頻文件。

????? 面對以上問題,Sunny軟件公司的開發(fā)人員該如何來解決?這就需要用到本章將要介紹的組合模式,組合模式為處理樹形結(jié)構(gòu)提供了一種較為完美的解決方案,它描述了如何將容器和葉子進(jìn)行遞歸組合,使得用戶在使用時無須對它們進(jìn)行區(qū)分,可以一致地對待容器和葉子

?

11.2 組合模式概述

????? 對于樹形結(jié)構(gòu),當(dāng)容器對象(如文件夾)的某一個方法被調(diào)用時,將遍歷整個樹形結(jié)構(gòu),尋找也包含這個方法的成員對象(可以是容器對象,也可以是葉子對象)并調(diào)用執(zhí)行,牽一而動百,其中使用了遞歸調(diào)用的機(jī)制來對整個結(jié)構(gòu)進(jìn)行處理。由于容器對象和葉子對象在功能上的區(qū)別,在使用這些對象的代碼中必須有區(qū)別地對待容器對象和葉子對象,而實際上大多數(shù)情況下我們希望一致地處理它們,因為對于這些對象的區(qū)別對待將會使得程序非常復(fù)雜。組合模式為解決此類問題而誕生,它可以讓葉子對象和容器對象的使用具有一致性。

????? 組合模式定義如下:

組合模式(Composite Pattern):組合多個對象形成樹形結(jié)構(gòu)以表示具有“整體—部分”關(guān)系的層次結(jié)構(gòu)。組合模式對單個對象(即葉子對象)和組合對象(即容器對象)的使用具有一致性,組合模式又可以稱為“整體—部分”(Part-Whole)模式,它是一種對象結(jié)構(gòu)型模式。

????? 在組合模式中引入了抽象構(gòu)件類Component,它是所有容器類和葉子類的公共父類,客戶端針對Component進(jìn)行編程。組合模式結(jié)構(gòu)如圖11-3所示:

圖11-3??組合模式結(jié)構(gòu)圖

????? 在組合模式結(jié)構(gòu)圖中包含如下幾個角色:

??????●?Component(抽象構(gòu)件):它可以是接口或抽象類,為葉子構(gòu)件和容器構(gòu)件對象聲明接口,在該角色中可以包含所有子類共有行為的聲明和實現(xiàn)。在抽象構(gòu)件中定義了訪問及管理它的子構(gòu)件的方法,如增加子構(gòu)件、刪除子構(gòu)件、獲取子構(gòu)件等。

??????●?Leaf(葉子構(gòu)件):它在組合結(jié)構(gòu)中表示葉子節(jié)點對象,葉子節(jié)點沒有子節(jié)點,它實現(xiàn)了在抽象構(gòu)件中定義的行為。對于那些訪問及管理子構(gòu)件的方法,可以通過異常等方式進(jìn)行處理。

??????●?Composite(容器構(gòu)件):它在組合結(jié)構(gòu)中表示容器節(jié)點對象,容器節(jié)點包含子節(jié)點,其子節(jié)點可以是葉子節(jié)點,也可以是容器節(jié)點,它提供一個集合用于存儲子節(jié)點,實現(xiàn)了在抽象構(gòu)件中定義的行為,包括那些訪問及管理子構(gòu)件的方法,在其業(yè)務(wù)方法中可以遞歸調(diào)用其子節(jié)點的業(yè)務(wù)方法。

??????組合模式的關(guān)鍵是定義了一個抽象構(gòu)件類,它既可以代表葉子,又可以代表容器,而客戶端針對該抽象構(gòu)件類進(jìn)行編程,無須知道它到底表示的是葉子還是容器,可以對其進(jìn)行統(tǒng)一處理。同時容器對象與抽象構(gòu)件類之間還建立一個聚合關(guān)聯(lián)關(guān)系,在容器對象中既可以包含葉子,也可以包含容器,以此實現(xiàn)遞歸組合,形成一個樹形結(jié)構(gòu)。

????? 如果不使用組合模式,客戶端代碼將過多地依賴于容器對象復(fù)雜的內(nèi)部實現(xiàn)結(jié)構(gòu),容器對象內(nèi)部實現(xiàn)結(jié)構(gòu)的變化將引起客戶代碼的頻繁變化,帶來了代碼維護(hù)復(fù)雜、可擴(kuò)展性差等弊端。組合模式的引入將在一定程度上解決這些問題。

????? 下面通過簡單的示例代碼來分析組合模式的各個角色的用途和實現(xiàn)。對于組合模式中的抽象構(gòu)件角色,其典型代碼如下所示:

abstract class Component { public abstract void add(Component c); //增加成員 public abstract void remove(Component c); //刪除成員 public abstract Component getChild(int i); //獲取成員 public abstract void operation(); //業(yè)務(wù)方法 }

一般將抽象構(gòu)件類設(shè)計為接口或抽象類,將所有子類共有方法的聲明和實現(xiàn)放在抽象構(gòu)件類中。對于客戶端而言,將針對抽象構(gòu)件編程,而無須關(guān)心其具體子類是容器構(gòu)件還是葉子構(gòu)件。

????? 如果繼承抽象構(gòu)件的是葉子構(gòu)件,則其典型代碼如下所示:

class Leaf extends Component { public void add(Component c) { //異常處理或錯誤提示 } public void remove(Component c) { //異常處理或錯誤提示 } public Component getChild(int i) { //異常處理或錯誤提示 return null; } public void operation() { //葉子構(gòu)件具體業(yè)務(wù)方法的實現(xiàn) } }

作為抽象構(gòu)件類的子類,在葉子構(gòu)件中需要實現(xiàn)在抽象構(gòu)件類中聲明的所有方法,包括業(yè)務(wù)方法以及管理和訪問子構(gòu)件的方法,但是葉子構(gòu)件不能再包含子構(gòu)件,因此在葉子構(gòu)件中實現(xiàn)子構(gòu)件管理和訪問方法時需要提供異常處理或錯誤提示。當(dāng)然,這無疑會給葉子構(gòu)件的實現(xiàn)帶來麻煩。

????? 如果繼承抽象構(gòu)件的是容器構(gòu)件,則其典型代碼如下所示:

class Composite extends Component { private ArrayList<Component> list = new ArrayList<Component>(); public void add(Component c) { list.add(c); } public void remove(Component c) { list.remove(c); } public Component getChild(int i) { return (Component)list.get(i); } public void operation() { //容器構(gòu)件具體業(yè)務(wù)方法的實現(xiàn) //遞歸調(diào)用成員構(gòu)件的業(yè)務(wù)方法 for(Object obj:list) { ((Component)obj).operation(); } } }

?在容器構(gòu)件中實現(xiàn)了在抽象構(gòu)件中聲明的所有方法,既包括業(yè)務(wù)方法,也包括用于訪問和管理成員子構(gòu)件的方法,如add()、remove()和getChild()等方法。需要注意的是在實現(xiàn)具體業(yè)務(wù)方法時,由于容器構(gòu)件充當(dāng)?shù)氖侨萜鹘巧?#xff0c;包含成員構(gòu)件,因此它將調(diào)用其成員構(gòu)件的業(yè)務(wù)方法。在組合模式結(jié)構(gòu)中,由于容器構(gòu)件中仍然可以包含容器構(gòu)件,因此在對容器構(gòu)件進(jìn)行處理時需要使用遞歸算法,即在容器構(gòu)件的operation()方法中遞歸調(diào)用其成員構(gòu)件的operation()方法。

?

?

思考

????? 在組合模式結(jié)構(gòu)圖中,如果聚合關(guān)聯(lián)關(guān)系不是從Composite到Component的,而是從Composite到Leaf的,如圖11-4所示,會產(chǎn)生怎樣的結(jié)果?

圖11-4???組合模式思考題結(jié)構(gòu)圖

?

11.3? 完整解決方案

????? 為了讓系統(tǒng)具有更好的靈活性和可擴(kuò)展性,客戶端可以一致地對待文件和文件夾,Sunny公司開發(fā)人員使用組合模式來進(jìn)行殺毒軟件的框架設(shè)計,其基本結(jié)構(gòu)如圖11-5所示:

圖11-5??殺毒軟件框架設(shè)計結(jié)構(gòu)圖

??? 在圖11-5中,?AbstractFile充當(dāng)抽象構(gòu)件類,Folder充當(dāng)容器構(gòu)件類,ImageFile、TextFile和VideoFile充當(dāng)葉子構(gòu)件類。完整代碼如下所示:

?

import java.util.*; //抽象文件類:抽象構(gòu)件 abstract class AbstractFile { public abstract void add(AbstractFile file); public abstract void remove(AbstractFile file); public abstract AbstractFile getChild(int i); public abstract void killVirus(); } //圖像文件類:葉子構(gòu)件 class ImageFile extends AbstractFile { private String name; public ImageFile(String name) { this.name = name; } public void add(AbstractFile file) { System.out.println("對不起,不支持該方法!"); } public void remove(AbstractFile file) { System.out.println("對不起,不支持該方法!"); } public AbstractFile getChild(int i) { System.out.println("對不起,不支持該方法!"); return null; } public void killVirus() { //模擬殺毒 System.out.println("----對圖像文件'" + name + "'進(jìn)行殺毒"); } } //文本文件類:葉子構(gòu)件 class TextFile extends AbstractFile { private String name; public TextFile(String name) { this.name = name; } public void add(AbstractFile file) { System.out.println("對不起,不支持該方法!"); } public void remove(AbstractFile file) { System.out.println("對不起,不支持該方法!"); } public AbstractFile getChild(int i) { System.out.println("對不起,不支持該方法!"); return null; } public void killVirus() { //模擬殺毒 System.out.println("----對文本文件'" + name + "'進(jìn)行殺毒"); } } //視頻文件類:葉子構(gòu)件 class VideoFile extends AbstractFile { private String name; public VideoFile(String name) { this.name = name; } public void add(AbstractFile file) { System.out.println("對不起,不支持該方法!"); } public void remove(AbstractFile file) { System.out.println("對不起,不支持該方法!"); } public AbstractFile getChild(int i) { System.out.println("對不起,不支持該方法!"); return null; } public void killVirus() { //模擬殺毒 System.out.println("----對視頻文件'" + name + "'進(jìn)行殺毒"); } } //文件夾類:容器構(gòu)件 class Folder extends AbstractFile { //定義集合fileList,用于存儲AbstractFile類型的成員 private ArrayList<AbstractFile> fileList=new ArrayList<AbstractFile>(); private String name; public Folder(String name) { this.name = name; } public void add(AbstractFile file) { fileList.add(file); } public void remove(AbstractFile file) { fileList.remove(file); } public AbstractFile getChild(int i) { return (AbstractFile)fileList.get(i); } public void killVirus() { System.out.println("****對文件夾'" + name + "'進(jìn)行殺毒"); //模擬殺毒 //遞歸調(diào)用成員構(gòu)件的killVirus()方法 for(Object obj : fileList) { ((AbstractFile)obj).killVirus(); } } }

?編寫如下客戶端測試代碼:

class Client { public static void main(String args[]) { //針對抽象構(gòu)件編程 AbstractFile file1,file2,file3,file4,file5,folder1,folder2,folder3,folder4; folder1 = new Folder("Sunny的資料"); folder2 = new Folder("圖像文件"); folder3 = new Folder("文本文件"); folder4 = new Folder("視頻文件"); file1 = new ImageFile("小龍女.jpg"); file2 = new ImageFile("張無忌.gif"); file3 = new TextFile("九陰真經(jīng).txt"); file4 = new TextFile("葵花寶典.doc"); file5 = new VideoFile("笑傲江湖.rmvb"); folder2.add(file1); folder2.add(file2); folder3.add(file3); folder3.add(file4); folder4.add(file5); folder1.add(folder2); folder1.add(folder3); folder1.add(folder4); //從“Sunny的資料”節(jié)點開始進(jìn)行殺毒操作 folder1.killVirus(); } }

?編譯并運行程序,輸出結(jié)果如下:

?

****對文件夾'Sunny的資料'進(jìn)行殺毒

****對文件夾'圖像文件'進(jìn)行殺毒

----對圖像文件'小龍女.jpg'進(jìn)行殺毒

----對圖像文件'張無忌.gif'進(jìn)行殺毒

****對文件夾'文本文件'進(jìn)行殺毒

----對文本文件'九陰真經(jīng).txt'進(jìn)行殺毒

----對文本文件'葵花寶典.doc'進(jìn)行殺毒

****對文件夾'視頻文件'進(jìn)行殺毒

----對視頻文件'笑傲江湖.rmvb'進(jìn)行殺毒

?

????? 由于在本實例中使用了組合模式,在抽象構(gòu)件類中聲明了所有方法,包括用于管理和訪問子構(gòu)件的方法,如add()方法和remove()方法等,因此在ImageFile等葉子構(gòu)件類中實現(xiàn)這些方法時必須進(jìn)行相應(yīng)的異常處理或錯誤提示。在容器構(gòu)件類Folder的killVirus()方法中將遞歸調(diào)用其成員對象的killVirus()方法,從而實現(xiàn)對整個樹形結(jié)構(gòu)的遍歷。

????? 如果需要更換操作節(jié)點,例如只需對文件夾“文本文件”進(jìn)行殺毒,客戶端代碼只需修改一行即可,將代碼:

?

folder1.killVirus();

?

???????改為:

?

folder3.killVirus();

?

???????輸出結(jié)果如下:

?

****對文件夾'文本文件'進(jìn)行殺毒

----對文本文件'九陰真經(jīng).txt'進(jìn)行殺毒

----對文本文件'葵花寶典.doc'進(jìn)行殺毒

?

???????在具體實現(xiàn)時,我們可以創(chuàng)建圖形化界面讓用戶選擇所需操作的根節(jié)點,無須修改源代碼,符合“開閉原則”,客戶端無須關(guān)心節(jié)點的層次結(jié)構(gòu),可以對所選節(jié)點進(jìn)行統(tǒng)一處理,提高系統(tǒng)的靈活性。

11.4? 透明組合模式與安全組合模式

????? 通過引入組合模式,Sunny公司設(shè)計的殺毒軟件具有良好的可擴(kuò)展性,在增加新的文件類型時,無須修改現(xiàn)有類庫代碼,只需增加一個新的文件類作為AbstractFile類的子類即可,但是由于在AbstractFile中聲明了大量用于管理和訪問成員構(gòu)件的方法,例如add()、remove()等方法,我們不得不在新增的文件類中實現(xiàn)這些方法,提供對應(yīng)的錯誤提示和異常處理。為了簡化代碼,我們有以下兩個解決方案:

??????解決方案一:將葉子構(gòu)件的add()、remove()等方法的實現(xiàn)代碼移至AbstractFile類中,由AbstractFile提供統(tǒng)一的默認(rèn)實現(xiàn),代碼如下所示:

//提供默認(rèn)實現(xiàn)的抽象構(gòu)件類 abstract class AbstractFile { public void add(AbstractFile file) { System.out.println("對不起,不支持該方法!"); } public void remove(AbstractFile file) { System.out.println("對不起,不支持該方法!"); } public AbstractFile getChild(int i) { System.out.println("對不起,不支持該方法!"); return null; } public abstract void killVirus(); }

如果客戶端代碼針對抽象類AbstractFile編程,在調(diào)用文件對象的這些方法時將出現(xiàn)錯誤提示。如果不希望出現(xiàn)任何錯誤提示,我們可以在客戶端定義文件對象時不使用抽象層,而直接使用具體葉子構(gòu)件本身,客戶端代碼片段如下所示:

class Client { public static void main(String args[]) { //不能透明處理葉子構(gòu)件 ImageFile file1,file2; TextFile file3,file4; VideoFile file5; AbstractFile folder1,folder2,folder3,folder4; //其他代碼省略 } }

? 這樣就產(chǎn)生了一種不透明的使用方式,即在客戶端不能全部針對抽象構(gòu)件類編程,需要使用具體葉子構(gòu)件類型來定義葉子對象。

??????解決方案二:除此之外,還有一種解決方法是在抽象構(gòu)件AbstractFile中不聲明任何用于訪問和管理成員構(gòu)件的方法,代碼如下所示:

abstract class AbstractFile { public abstract void killVirus(); }

此時,由于在AbstractFile中沒有聲明add()、remove()等訪問和管理成員的方法,其葉子構(gòu)件子類無須提供實現(xiàn);而且無論客戶端如何定義葉子構(gòu)件對象都無法調(diào)用到這些方法,不需要做任何錯誤和異常處理,容器構(gòu)件再根據(jù)需要增加訪問和管理成員的方法,但這時候也存在一個問題:客戶端不得不使用容器類本身來聲明容器構(gòu)件對象,否則無法訪問其中新增的add()、remove()等方法,如果客戶端一致性地對待葉子和容器,將會導(dǎo)致容器構(gòu)件的新增對客戶端不可見,客戶端代碼對于容器構(gòu)件無法再使用抽象構(gòu)件來定義,客戶端代碼片段如下所示:

class Client { public static void main(String args[]) { AbstractFile file1,file2,file3,file4,file5; Folder folder1,folder2,folder3,folder4; //不能透明處理容器構(gòu)件 //其他代碼省略 } }

?在使用組合模式時,根據(jù)抽象構(gòu)件類的定義形式,我們可將組合模式分為透明組合模式和安全組合模式兩種形式:

????? (1)?透明組合模式

????? 透明組合模式中,抽象構(gòu)件Component中聲明了所有用于管理成員對象的方法,包括add()、remove()以及getChild()等方法,這樣做的好處是確保所有的構(gòu)件類都有相同的接口。在客戶端看來,葉子對象與容器對象所提供的方法是一致的,客戶端可以相同地對待所有的對象。透明組合模式也是組合模式的標(biāo)準(zhǔn)形式,雖然上面的解決方案一在客戶端可以有不透明的實現(xiàn)方法,但是由于在抽象構(gòu)件中包含add()、remove()等方法,因此它還是透明組合模式,透明組合模式的完整結(jié)構(gòu)如圖11-6所示:

圖11-6??透明組合模式結(jié)構(gòu)圖

??????透明組合模式的缺點是不夠安全,因為葉子對象和容器對象在本質(zhì)上是有區(qū)別的。葉子對象不可能有下一個層次的對象,即不可能包含成員對象,因此為其提供add()、remove()以及getChild()等方法是沒有意義的,這在編譯階段不會出錯,但在運行階段如果調(diào)用這些方法可能會出錯(如果沒有提供相應(yīng)的錯誤處理代碼)。

????? (2)?安全組合模式

????? 安全組合模式中,在抽象構(gòu)件Component中沒有聲明任何用于管理成員對象的方法,而是在Composite類中聲明并實現(xiàn)這些方法。這種做法是安全的,因為根本不向葉子對象提供這些管理成員對象的方法,對于葉子對象,客戶端不可能調(diào)用到這些方法,這就是解決方案二所采用的實現(xiàn)方式。安全組合模式的結(jié)構(gòu)如圖11-7所示:

圖11-7??安全組合模式結(jié)構(gòu)圖

?????? 安全組合模式的缺點是不夠透明,因為葉子構(gòu)件和容器構(gòu)件具有不同的方法,且容器構(gòu)件中那些用于管理成員對象的方法沒有在抽象構(gòu)件類中定義,因此客戶端不能完全針對抽象編程,必須有區(qū)別地對待葉子構(gòu)件和容器構(gòu)件。在實際應(yīng)用中,安全組合模式的使用頻率也非常高,在Java?AWT中使用的組合模式就是安全組合模式。

11.5 公司組織結(jié)構(gòu)

???????在學(xué)習(xí)和使用組合模式時,Sunny軟件公司開發(fā)人員發(fā)現(xiàn)樹形結(jié)構(gòu)其實隨處可見,例如Sunny公司的組織結(jié)構(gòu)就是“一棵標(biāo)準(zhǔn)的樹”,如圖11-8所示:

圖11-8? Sunny公司組織結(jié)構(gòu)圖

????? 在Sunny軟件公司的內(nèi)部辦公系統(tǒng)Sunny OA系統(tǒng)中,有一個與公司組織結(jié)構(gòu)對應(yīng)的樹形菜單,行政人員可以給各級單位下發(fā)通知,這些單位可以是總公司的一個部門,也可以是一個分公司,還可以是分公司的一個部門。用戶只需要選擇一個根節(jié)點即可實現(xiàn)通知的下發(fā)操作,而無須關(guān)心具體的實現(xiàn)細(xì)節(jié)。這不正是組合模式的“特長”嗎?于是Sunny公司開發(fā)人員繪制了如圖11-9所示結(jié)構(gòu)圖:

圖11-9 ?Sunny公司組織結(jié)構(gòu)組合模式示意圖

???????在圖11-9中,“單位”充當(dāng)了抽象構(gòu)件角色,“公司”充當(dāng)了容器構(gòu)件角色,“研發(fā)部”、“財務(wù)部”和“人力資源部”充當(dāng)了葉子構(gòu)件角色。

?

思考

如何編碼實現(xiàn)圖11-9中的“公司”類?

?

11.6 組合模式總結(jié)

????? 組合模式使用面向?qū)ο蟮乃枷雭韺崿F(xiàn)樹形結(jié)構(gòu)的構(gòu)建與處理,描述了如何將容器對象和葉子對象進(jìn)行遞歸組合,實現(xiàn)簡單,靈活性好。由于在軟件開發(fā)中存在大量的樹形結(jié)構(gòu),因此組合模式是一種使用頻率較高的結(jié)構(gòu)型設(shè)計模式,Java SE中的AWT和Swing包的設(shè)計就基于組合模式,在這些界面包中為用戶提供了大量的容器構(gòu)件(如Container)和成員構(gòu)件(如Checkbox、Button和TextComponent等),其結(jié)構(gòu)如圖11-10所示:

圖11-10 AWT組合模式結(jié)構(gòu)示意圖

????? 在圖11-10中,Component類是抽象構(gòu)件,Checkbox、Button和TextComponent是葉子構(gòu)件,而Container是容器構(gòu)件,在AWT中包含的葉子構(gòu)件還有很多,因為篇幅限制沒有在圖中一一列出。在一個容器構(gòu)件中可以包含葉子構(gòu)件,也可以繼續(xù)包含容器構(gòu)件,這些葉子構(gòu)件和容器構(gòu)件一起組成了復(fù)雜的GUI界面。

????? 除此以外,在XML解析、組織結(jié)構(gòu)樹處理、文件系統(tǒng)設(shè)計等領(lǐng)域,組合模式都得到了廣泛應(yīng)用。

????? 1.?主要優(yōu)點

????? 組合模式的主要優(yōu)點如下:

????? (1)?組合模式可以清楚地定義分層次的復(fù)雜對象,表示對象的全部或部分層次,它讓客戶端忽略了層次的差異,方便對整個層次結(jié)構(gòu)進(jìn)行控制。

????? (2)?客戶端可以一致地使用一個組合結(jié)構(gòu)或其中單個對象,不必關(guān)心處理的是單個對象還是整個組合結(jié)構(gòu),簡化了客戶端代碼。

????? (3)?在組合模式中增加新的容器構(gòu)件和葉子構(gòu)件都很方便,無須對現(xiàn)有類庫進(jìn)行任何修改,符合“開閉原則”。

????? (4)?組合模式為樹形結(jié)構(gòu)的面向?qū)ο髮崿F(xiàn)提供了一種靈活的解決方案,通過葉子對象和容器對象的遞歸組合,可以形成復(fù)雜的樹形結(jié)構(gòu),但對樹形結(jié)構(gòu)的控制卻非常簡單。

????? 2.?主要缺點

????? 組合模式的主要缺點如下:

????? 在增加新構(gòu)件時很難對容器中的構(gòu)件類型進(jìn)行限制。有時候我們希望一個容器中只能有某些特定類型的對象,例如在某個文件夾中只能包含文本文件,使用組合模式時,不能依賴類型系統(tǒng)來施加這些約束,因為它們都來自于相同的抽象層,在這種情況下,必須通過在運行時進(jìn)行類型檢查來實現(xiàn),這個實現(xiàn)過程較為復(fù)雜。

????? 3.?適用場景

????? 在以下情況下可以考慮使用組合模式:

????? (1)?在具有整體和部分的層次結(jié)構(gòu)中,希望通過一種方式忽略整體與部分的差異,客戶端可以一致地對待它們。

????? (2)?在一個使用面向?qū)ο笳Z言開發(fā)的系統(tǒng)中需要處理一個樹形結(jié)構(gòu)。

????? (3)?在一個系統(tǒng)中能夠分離出葉子對象和容器對象,而且它們的類型不固定,需要增加一些新的類型。

?

練習(xí)

Sunny軟件公司欲開發(fā)一個界面控件庫,界面控件分為兩大類,一類是單元控件,例如按鈕、文本框等,一類是容器控件,例如窗體、中間面板等,試用組合模式設(shè)計該界面控件庫。

?

?

【作者:劉偉 ?http://blog.csdn.net/lovelion】

轉(zhuǎn)自:https://www.cnblogs.com/lfxiao/p/6816026.html

轉(zhuǎn)載于:https://www.cnblogs.com/itplay/p/9639593.html

與50位技術(shù)專家面對面20年技術(shù)見證,附贈技術(shù)全景圖

總結(jié)

以上是生活随笔為你收集整理的1、【设计模式】组合模式的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。