組合模式-統一的處理個別物件與組合物件

碼農充電站發表於2021-01-13

公號:碼農充電站pro
主頁:https://codeshellme.github.io

本篇來介紹組合模式Composite Design Pattern)。

1,組合模式

組合模式可以將物件組合成樹形結構來表示“整體-部分”的層次結構,使得客戶可以用一致的方式處理個別物件和物件組合。

如果物件之間呈樹形結構,是一種部分與整體的關係,這種情況都比較適合用組合模式。

組合模式構建的樹狀關係如下所示:

在這裡插入圖片描述

組合模式使得我們構建這種樹狀關係變得極為簡單。aComposite 是組合物件,aLeaf 是個體物件。組合物件中可以巢狀組合物件和個體物件,個體物件中不能再包含其它物件。

因為組合物件中可以再次巢狀組合物件,所以組合模式也是一種遞迴關係

組合模式的類圖如下:

在這裡插入圖片描述

從類圖中可以看到,Component 為所有的物件提供了統一的介面。組合物件中有 addremove 操作,說明組合物件中可以新增和刪除組合物件與個體物件。

注意 Leaf 節點中只有 operation 操作,因為 Leaf 節點不能再巢狀其它節點。

組合物件和個體物件中都有 operation 操作,使得我們能夠把相同的操作應用在組合物件和個別物件上。

2,組合模式示例

下面舉個例子,來看下如何使用組合模式。

我們已經知道,組合模式非常適合表示樹形結構,而 Linux 目錄結構就是一個樹形結構。下面就用組合模式來構建目錄結構。

我們知道目錄中即可包含目錄,也可包含檔案。根據組合模式的類圖,可設計出下面的 Linux 檔案系統類圖:

在這裡插入圖片描述

檔案系統可以包括檔案和目錄,目錄中可以巢狀目錄和檔案,而檔案中不能再巢狀其它東西。所以,File 類中沒有 addremove 方法。

首先建立 FileSystem 抽象類:

abstract class FileSystem {
    protected String path;

    public abstract void printFile();
}

printFile 方法用於輸出檔名。

再建立 File 類:

class File extends FileSystem {
    public File(String path) {
        this.path = path;
    }

    public void printFile() {
        System.out.println(path);
    }
}

File 類繼承了 FileSystem, 因為 File 類本身就是檔案,所以它的 printFile 非常簡單。

再建立 Directory 類:

class Directory extends FileSystem {
    private List<FileSystem> nodes; // 用於儲存節點

    public Directory(String path) {
        this.path = path;
        this.nodes = new ArrayList<>();
    }

    public void printFile() {
        System.out.println(path);

		// 遞迴輸出目錄和檔案
        for (FileSystem node: nodes) {
            node.printFile();
        }
    }

    public void addNode(FileSystem node) {
        nodes.add(node);
    }

    public void removeNode(FileSystem node) {
        nodes.remove(node);
    }
}

注意區分 File 類和 Directory 類的不同。

下面來測試程式碼,假設我們要構建這樣的目錄結構:

test/
├── a
├── b
│   ├── 1.txt
│   └── d
│       └── 2.txt
└── c
    └── 3.txt

首先建立檔案節點和目錄節點:

// 建立檔案節點
File txt1 = new File("test/b/1.txt");
File txt2 = new File("test/b/d/2.txt");
File txt3 = new File("test/c/3.txt");

// 建立目錄節點
Directory test = new Directory("test/");
Directory a = new Directory("test/a/");
Directory b = new Directory("test/b/");
Directory c = new Directory("test/c/");
Directory d = new Directory("test/b/d/");

構建目錄結構:

// 構造目錄結構
test.addNode(a);
test.addNode(b);
test.addNode(c);

b.addNode(txt1);
b.addNode(d);

c.addNode(txt3);
d.addNode(txt2);

輸出 test 目錄:

test.printFile();

結果如下:

test/
test/a/
test/b/
test/b/1.txt
test/b/d/
test/b/d/2.txt
test/c/
test/c/3.txt

輸出 b 目錄:

b.printFile();

結果如下:

test/b/
test/b/1.txt
test/b/d/
test/b/d/2.txt

刪除 b 目錄後,在輸出 test 目錄:

test.removeNode(b);
test.printFile();

結果如下:

test/
test/a/
test/c/
test/c/3.txt

通過測試結果可以看到,我們構建的目錄結構是沒有問題的。

我將完整的程式碼放在了這裡,供大家參考。

3,總結

組合模式將一組物件組織成樹形結構,物件分為個體物件和組合物件,組合物件中可以包含個體物件和組合物件。組合模式會以遞迴的方式來處理樹的節點。

使用組合模式的前提是,物件之間的關係要符合樹形結構的特點。組合模式使得處理樹形結構的物件關係變得非常簡單。

(本節完。)


推薦閱讀:

外觀模式-簡化子系統的複雜性

模板方法模式-封裝一套演算法流程

迭代器模式-統一集合的遍歷方式

狀態模式-將狀態和行為封裝成物件

代理模式-訪問物件的代理而非其本身


歡迎關注作者公眾號,獲取更多技術乾貨。

碼農充電站pro

相關文章