一文徹底搞明白組合模式
本篇講解Java設(shè)計(jì)模式中的組合模式,分為定義、模式應(yīng)用前案例、結(jié)構(gòu)、模式應(yīng)用后案例、適用場(chǎng)景、模式可能存在的困惑和本質(zhì)探討7個(gè)部分。
定義
組合模式是將對(duì)象組合成樹(shù)形結(jié)構(gòu)以表示“部分-整體”的層次結(jié)構(gòu)。組合模式使得客戶對(duì)單個(gè)對(duì)象和復(fù)合對(duì)象的使用具有一致性。
在新的分類方式中,組合模式被劃分至類之間的交互類別中,其簡(jiǎn)化的是調(diào)用方與具備樹(shù)結(jié)構(gòu)的一組對(duì)象之間的交互,具體通過(guò)一致性的行為實(shí)現(xiàn)。

模式應(yīng)用前案例
下面以一個(gè)典型的文件和目錄為例來(lái)進(jìn)行說(shuō)明,先來(lái)看一下未應(yīng)用組合模式之前的代碼實(shí)現(xiàn)。
public class File {//文件結(jié)構(gòu)
    private final String name;
    public File(String name) {
        this.name = name;
    }
    public void display() {
        System.out.println("File: " + this.name);
    }
}
public class Directory {//目錄結(jié)構(gòu)
    private String name;
    private final List<File> files;
    private final List<Directory> directories;
    // 初始化方法
    public Directory(String name){
        this.name = this.name;
        this.files = new ArrayList<>();
        this.directories = new ArrayList<>();
    }
    // 添加子節(jié)點(diǎn)
    public void addFile(File file){
        this.files.add(file);
    }
    // 添加子目錄
    public void addDirectory(Directory directory) {
        this.directories.add(directory);
    }
    public void display(){
        //System.out.println("Directory:"+this.name);
        for(File file : this.files){
            file.display();
        }
        for (Directory dir : this.directories) {
            dir.display();
        }
    }
}
public class Client {//調(diào)用方代碼
    public static void main(String[] ars){
        Directory root= new Directory("Root");
        File file1=new File("file1.txt");
        File file2=new File("file2.txt");
        root.addFile(file1);
        root.addFile(file2);
        Directory subDirecory =new Directory ("Subdirectory");
        File file3 = new File("file3.tx");
        File file4 = new File("file4.tx");
        subDirecory.addFile(file3);
        subDirecory.addFile(file4);
        root.addDirectory(subDirecory);
        root.display();
    }
}我們知道,文件和目錄兩者是一個(gè)大的樹(shù)結(jié)構(gòu)中的節(jié)點(diǎn)。在上面未使用組合模式的代碼中,文件和目錄都有自己定義的方法。這樣在構(gòu)建一個(gè)多層樹(shù)結(jié)構(gòu)的過(guò)程中,復(fù)雜度會(huì)提升。
結(jié)構(gòu)

組合模式的示例代碼如下。
public interface Component {
    void operation();
    void add(Component component);
    void remove(Component component);
    Component Display(int index);
}
public class Leaf implements Component{
    private String name;
    public Leaf(String name) {
        this.name = name;
    }
    @Override
    public void operation() {
        System.out.println("Leaf: " + name + " operation()");
    }
    @Override
    public void add(Component component) {
        throw new UnsupportedOperationException("Leaf cannot have children");
    }
    @Override
    public void remove(Component component) {
        throw new UnsupportedOperationException("Leaf cannot remove children");
    }
    @Override
    public Component Display(int index) {
        throw new UnsupportedOperationException("Leaf cannot get child");
    }
}
public interface Component {
    void operation();
    void add(Component component);
    void remove(Component component);
    Component Display(int index);
}
public class Client {
    public static void main(String[] args) {
        // 創(chuàng)建葉子節(jié)點(diǎn)
        Component leaf1 = new Leaf("LeafA");
        Component leaf2 = new Leaf("LeafB");
        Component leaf3 = new Leaf("LeafC");
        // 創(chuàng)建復(fù)合節(jié)點(diǎn)
        Component composite = new Composite("CompositeX");
        composite.add(leaf1);
        composite.add(leaf2);
        // 創(chuàng)建另一個(gè)復(fù)合節(jié)點(diǎn),并添加之前的復(fù)合節(jié)點(diǎn)和新的葉子節(jié)點(diǎn)
        Component root = new Composite("Root");
        root.add(composite);
        root.add(leaf3);
        // 執(zhí)行操作
        root.operation();
    }
}模式應(yīng)用后案例
上面文件與目錄的案例,使用組合模式之后的代碼實(shí)現(xiàn)如下。
public interface IComponent {//接口
    void display();
}
public class File implements IComponent{//文件實(shí)現(xiàn)
    private final String name;
    public File(String name) {
        this.name = name;
    }
    @Override
    public void display() {
        System.out.println("File: " + this.name);
    }
}
public class Directory implements IComponent{//目錄實(shí)現(xiàn)
    private String name;
    private final List<IComponent> children;
    // 初始化方法
    public Directory(String name){
        this.name = this.name;
        this.children = new ArrayList<>();
    }
    // 添加子節(jié)點(diǎn)
    public void addComponent(IComponent component){
        this.children.add(component);
    }
    // 顯示目錄內(nèi)容
    @Override
    public void display() {
       //System.out.println("Directory: " + this.name);
        for (IComponent child : this.children) {
            child.display();
        }
    }
}
public class Client {//調(diào)用方代碼
    public static void main(String[] ars){
        Directory root= new Directory("Root");
        File file1 = new File("file1.txt");
        File file2 = new File ("file2.txt");
        root.addComponent(file1);
        root.addComponent(file2);
        Directory subDirectory =new Directory ("Subdirectory");
        File file3 = new File("file3.txt");
        File file4 = new File("file4.txt");
        subDirectory.addComponent(file3);
        subDirectory.addComponent(file4);
        root.addComponent(subDirectory);
        root.display();
    }
}在上述代碼中,由于樹(shù)的結(jié)構(gòu)使用一個(gè)接口和實(shí)現(xiàn)的家族來(lái)實(shí)現(xiàn),這樣樹(shù)的結(jié)構(gòu)中所有類的行為都是一致的,簡(jiǎn)化了編碼時(shí)的復(fù)雜度。
適用場(chǎng)景
當(dāng)需求中出現(xiàn)的一系列概念或?qū)ο?,它們之間存在部分-整體的層次結(jié)構(gòu)或共同構(gòu)成一顆樹(shù)的結(jié)構(gòu)時(shí),就可以考慮使用組合模式。
模式可能存在的困惑
困惑1:組合模式中的“組合”,與“組合優(yōu)于繼承”中的“組合”,有什么關(guān)聯(lián)?
兩者都代表了一種關(guān)系。前者的“組合”指的是將一系列對(duì)象按照層次化結(jié)構(gòu)進(jìn)行組織。而后者的“組合”指的是兩個(gè)對(duì)象之間的聚合或組合關(guān)系,以此來(lái)取代類之間繼承關(guān)系。
本質(zhì)
組合模式的本質(zhì)在于提供了一種機(jī)制來(lái)處理對(duì)象之間的部分-整體關(guān)系,并且通過(guò)統(tǒng)一接口來(lái)簡(jiǎn)化調(diào)用方使用復(fù)雜層次結(jié)構(gòu)時(shí)可能遇到的問(wèn)題。















 
 
 











 
 
 
 