Java 組合模式及其應用

大雄45發表於2021-06-02
導讀 組合模式,就是在一個物件中包含其他物件,這些被包含的物件可能是終點物件(不再包含別的物件),也有可能是非終點物件(其內部還包含其他物件,或叫組物件),我們將物件稱為節點,即一個根節點包含許多子節點,這些子節點有的不再包含子節點,而有的仍然包含子節點,以此類推。

Java 組合模式及其應用Java 組合模式及其應用

很明顯,這是樹形結構,終結點叫葉子節點,非終節點(組節點)叫樹枝節點,第一個節點叫根節點。同時也類似於檔案目錄的結構形式:檔案可稱之為終節點,目錄可稱之為非終節點(組節點)。

普通實現

我們首先來看一個目錄結構的普通實現:
目錄節點:Noder

import java.util.ArrayList;
import java.util.List;
/**
 * 目錄節點
 * 包含:
 *         1、目錄名
 *         2、下級檔案列表
 *         3、下級目錄列表
 *         4、新增檔案方法
 *         5、新增目錄方法
 *         6、顯示下級內容方法
 */
public class Noder {
    String nodeName;//目錄名
    //透過構造器為目錄命名
    public Noder(String nodeName){
        this.nodeName = nodeName;
    }
    ListnodeList = new ArrayList();//目錄的下級目錄列表
    ListfileList = new ArrayList();//目錄的下級檔案列表
    //新增下級目錄
    public void addNoder(Noder noder){
        nodeList.add(noder);
    }
    //新增檔案
    public void addFiler(Filer filer){
        fileList.add(filer);
    }
    //顯示下級目錄及檔案
    public void display(){
        for(Noder noder:nodeList){
            System.out.println(noder.nodeName);
            noder.display();//遞迴顯示目錄列表
        }
        for(Filer filer:fileList){
            filer.display();
        }
    }
}

檔案節點:Filer

/**
 * 檔案節點
 * 檔案節點是終節點,無下級節點
 * 包含:
 *         1、檔名
 *         2、檔案顯示方法
 */
public class Filer {
    String fileName;//檔名
    public Filer(String fileName){
        this.fileName = fileName;
    }
    //檔案顯示方法
    public void display(){
        System.out.println(fileName);
    }
}

測試類:Clienter

import java.io.File;
 
public class Clienter {
    public static void createTree(Noder node){
        File file = new File(node.nodeName);
        File[] f = file.listFiles();
        for(File fi : f){
            if(fi.isFile()){
                Filer filer = new Filer(fi.getAbsolutePath());
                node.addFiler(filer);
            }
            if(fi.isDirectory()){
                Noder noder = new Noder(fi.getAbsolutePath());
                node.addNoder(noder);
                createTree(noder);//使用遞迴生成樹結構
            }
        }
    }
    public static void main(String[] args) {
        Noder noder = new Noder("E://ceshi");
        createTree(noder);//建立目錄樹形結構
        noder.display();//顯示目錄及檔案
    }
}

執行結果:

E:\ceshi\目錄1
E:\ceshi\目錄1\目錄3
E:\ceshi\目錄1\檔案2.txt
E:\ceshi\目錄2
E:\ceshi\目錄2\檔案3.txt
E:\ceshi\檔案1.txt
組合模式

從上面的程式碼中可以看出,我們分別定義了檔案節點物件與目錄節點物件,這是因為檔案與目錄之間的操作不同,檔案沒有下級節點,而目錄可以有下級節點,但是我們能不能這麼想:既然檔案與目錄都是可以作為一個節點的下級節點而存在,那麼我們可不可以將二者抽象為一類物件,雖然二者的操作不同,但是我們可以在實現類的方法實現中具體定義,比如檔案沒有新增下級節點的方法,我們就可以在檔案的這個方法中丟擲一個異常,不做具體實現,而在目錄中則具體實現新增操作。顯示操作二者都有,可以各自實現。而且由於我們將檔案與目錄抽象為一個型別,那麼結合多型我們可以進行如下實現:

抽象類:Node

/**
 * 將檔案與目錄統一看作是一類節點,做一個抽象類來定義這種節點,然後以其實現類來區分檔案與目錄,在實現類中分別定義各自的具體實現內容
 */
public abstract class Node {
    protected String name;//名稱
    //構造器賦名
    public Node(String name){
        this.name = name;
    }
    //新增節點:檔案節點無此方法,目錄節點重寫此方法
    public void addNode(Node node) throws Exception{
        throw new Exception("Invalid exception");
    }
    //顯示節點:檔案與目錄均實現此方法
    abstract void display();
}

檔案實現類:Filter

/**
 * 實現檔案節點
 */
public class Filer extends Node {
    //透過構造器為檔案節點命名
    public Filer(String name) {
        super(name);
    }
    //顯示檔案節點
    @Override
    public void display() {
        System.out.println(name);
    }
}

目錄實現類:Noder

import java.util.*;
/**
 * 實現目錄節點
 */
public class Noder extends Node {
    ListnodeList = new ArrayList();//內部節點列表(包括檔案和下級目錄)
    //透過構造器為當前目錄節點賦名
    public Noder(String name) {
        super(name);
    }
    //新增節點
    public void addNode(Node node) throws Exception{
        nodeList.add(node);
    }
    //遞迴迴圈顯示下級節點
    @Override
    void display() {
        System.out.println(name);
        for(Node node:nodeList){
            node.display();
        }
    }
}

測試類:Clienter

import java.io.File;
 
public class Clienter {
    public static void createTree(Node node) throws Exception{
        File file = new File(node.name);
        File[] f = file.listFiles();
        for(File fi : f){
            if(fi.isFile()){
                Filer filer = new Filer(fi.getAbsolutePath());
                node.addNode(filer);
            }
            if(fi.isDirectory()){
                Noder noder = new Noder(fi.getAbsolutePath());
                node.addNode(noder);
                createTree(noder);//使用遞迴生成樹結構
            }
        }
    }
    public static void main(String[] args) {
        Node noder = new Noder("E://ceshi");
        try {
            createTree(noder);
        } catch (Exception e) {
            e.printStackTrace();
        }
        noder.display();
    }
}

執行輸出結果:

E://ceshi
E:\ceshi\檔案1.txt
E:\ceshi\目錄1
E:\ceshi\目錄1\檔案2.txt
E:\ceshi\目錄1\目錄3
E:\ceshi\目錄2
E:\ceshi\目錄2\檔案3.txt

從上述實現中可以看出:所謂組合模式,其實說的是物件包含物件的問題,透過組合的方式(在物件內部引用物件)來進行佈局,我認為這種組合是區別於繼承的,而另一層含義是指樹形結構子節點的抽象(將葉子節點與數枝節點抽象為子節點),區別於普通的分別定義葉子節點與數枝節點的方式。

組合模式應用場景

這種組合模式正是應樹形結構而生,所以組合模式的使用場景就是出現樹形結構的地方。比如:檔案目錄顯示,多及目錄呈現等樹形結構資料的操作。

原文來自:


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69955379/viewspace-2775181/,如需轉載,請註明出處,否則將追究法律責任。

相關文章