樹形結構在軟件中隨處可見,例如操做系統中的目錄結構、應用軟件中的菜單、辦公系統中的公司組織結構等等,如何運用面向對象的方式來處理這種樹形結構是組合模式須要解決的問題,組合模式經過一種巧妙的設計方案使得用戶能夠一致性地處理整個樹形結構或者樹形結構的一部分,也能夠一致性地處理樹形結構中的葉子節點(不包含子節點的節點)和容器節點(包含子節點的節點)。下面將學習這種用於處理樹形結構的組合模式。
11.1 設計殺毒軟件的框架結構
Sunny軟件公司欲開發一個殺毒(AntiVirus)軟件,該軟件既能夠對某個文件夾(Folder)殺毒,也能夠對某個指定的文件(File)進行殺毒。該殺毒軟件還能夠根據各種文件的特色,爲不一樣類型的文件提供不一樣的殺毒方式,例如圖像文件(ImageFile)和文本文件(TextFile)的殺毒方式就有所差別。現須要提供該殺毒軟件的總體框架設計方案。java |
在介紹Sunny公司開發人員提出的初始解決方案以前,咱們先來分析一下操做系統中的文件目錄結構,例如在Windows操做系統中,存在如圖11-1所示目錄結構:框架
圖11-1 Windows目錄結構ide
圖11-1能夠簡化爲如圖11-2所示樹形目錄結構:學習
圖11-2 樹形目錄結構示意圖測試
咱們能夠看出,在圖11-2中包含文件(灰色節點)和文件夾(白色節點)兩類不一樣的元素,其中在文件夾中能夠包含文件,還能夠繼續包含子文件夾,可是在文件中不能再包含子文件或者子文件夾。在此,咱們能夠稱文件夾爲容器(Container),而不一樣類型的各類文件是其成員,也稱爲葉子(Leaf),一個文件夾也能夠做爲另外一個更大的文件夾的成員。若是咱們如今要對某一個文件夾進行操做,如查找文件,那麼須要對指定的文件夾進行遍歷,若是存在子文件夾則打開其子文件夾繼續遍歷,若是是文件則判斷以後返回查找結果。this
Sunny軟件公司的開發人員經過分析,決定使用面向對象的方式來實現對文件和文件夾的操做,定義了以下圖像文件類ImageFile、文本文件類TextFile和文件夾類Folder:spa
- //爲了突出核心框架代碼,咱們對殺毒過程的實現進行了大量簡化
- import java.util.*;
-
- //圖像文件類
- class ImageFile {
- private String name;
-
- public ImageFile(String name) {
- this.name = name;
- }
-
- public void killVirus() {
- //簡化代碼,模擬殺毒
- System.out.println("----對圖像文件'" + name + "'進行殺毒");
- }
- }
-
- //文本文件類
- class TextFile {
- private String name;
-
- public TextFile(String name) {
- this.name = name;
- }
-
- public void killVirus() {
- //簡化代碼,模擬殺毒
- System.out.println("----對文本文件'" + name + "'進行殺毒");
- }
- }
-
- //文件夾類
- class Folder {
- private String name;
- //定義集合folderList,用於存儲Folder類型的成員
- private ArrayList folderList = new ArrayList();
-
- //定義集合imageList,用於存儲ImageFile類型的成員
- private ArrayList imageList = new ArrayList();
-
- //定義集合textList,用於存儲TextFile類型的成員
- private ArrayList textList = new ArrayList();
-
- 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 + "'進行殺毒"); //模擬殺毒
-
- //若是是Folder類型的成員,遞歸調用Folder的killVirus()方法
- for(Object obj : folderList) {
- ((Folder)obj).killVirus();
- }
-
- //若是是ImageFile類型的成員,調用ImageFile的killVirus()方法
- for(Object obj : imageList) {
- ((ImageFile)obj).killVirus();
- }
-
- //若是是TextFile類型的成員,調用TextFile的killVirus()方法
- for(Object obj : textList) {
- ((TextFile)obj).killVirus();
- }
- }
- }
編寫以下客戶端測試代碼進行測試:操作系統
- 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("九陰真經.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();
- }
- }
編譯並運行程序,輸出結果以下:.net
****對文件夾'Sunny的資料'進行殺毒設計 ****對文件夾'圖像文件'進行殺毒 ----對圖像文件'小龍女.jpg'進行殺毒 ----對圖像文件'張無忌.gif'進行殺毒 ****對文件夾'文本文件'進行殺毒 ----對文本文件'九陰真經.txt'進行殺毒 ----對文本文件'葵花寶典.doc'進行殺毒 |
Sunny公司開發人員「成功」實現了殺毒軟件的框架設計,但經過仔細分析,發現該設計方案存在以下問題:
(1) 文件夾類Folder的設計和實現都很是複雜,須要定義多個集合存儲不一樣類型的成員,並且須要針對不一樣的成員提供增長、刪除和獲取等管理和訪問成員的方法,存在大量的冗餘代碼,系統維護較爲困難;
(2) 因爲系統沒有提供抽象層,客戶端代碼必須有區別地對待充當容器的文件夾Folder和充當葉子的ImageFile和TextFile,沒法統一對它們進行處理;
(3) 系統的靈活性和可擴展性差,若是須要增長新的類型的葉子和容器都須要對原有代碼進行修改,例如若是須要在系統中增長一種新類型的視頻文件VideoFile,則必須修改Folder類的源代碼,不然沒法在文件夾中添加視頻文件。
面對以上問題,Sunny軟件公司的開發人員該如何來解決?這就須要用到本章將要介紹的組合模式,組合模式爲處理樹形結構提供了一種較爲完美的解決方案,它描述瞭如何將容器和葉子進行遞歸組合,使得用戶在使用時無須對它們進行區分,能夠一致地對待容器和葉子。
【做者:劉偉 http://blog.csdn.net/lovelion】