# 组合模式

组合模式：将对象组合成**树形结构**以表示"部分-整体"的层次结构，使得客户端对单个对象和组合对象的使用具有一致性。

组合模式的关键是抽象出一个类，这个类既可以表示叶子节点，也可以表示组合对象。

理解组合模式的关键词：抽象类、树形结构、客户端统一操作即面向抽象（类）编程

**组合模式的中的角色**

* Component（抽象构件）：接口或抽象类，为Leaf和Composite声明共有行为的方法以及部分方法实现：如增加子构件、删除子构件、获取子构件等。
* Leaf（叶子构件）：叶子节点对象，没有子节点。
* Composite（容器构件）：容器节点对象，包含子节点（子节点可以是Leaf，也可以是Composite），提供一个集合用于存储子节点。

**组合模式结构代码**

Component

```java
public 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();  //Leaf和Composite共有的业务方法  
}
```

Leaf：由于Leaf没有add、remove、getChild等操作，所以在实现中可以抛出UnSupportOperationException或直接为空。

```java
public class Leaf extends Component {  

    public void add(Component c) {   
        //异常处理或错误提示   
    }     

    public void remove(Component c) {   
        //异常处理或错误提示   
    }  

    public Component getChild(int i) {   
        //异常处理或错误提示  
        return null;   
    }

    public void operation() {  
        //叶子构件具体业务方法的实现  
    }   
}
```

Composite

```java
public 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() {  
        //容器构件具体业务方法的实现  
        //递归调用成员构件的业务方法  
        for(Object obj:list) {  
            ((Component)obj).operation();  
        }  
    }     
}
```

现实中，电脑上的目录，公司中的部门，软件中的菜单，都是这种树形结构的形式，都可以考虑使用组合模式来简化客户端的操作。

举个例子：开发一个杀毒(AntiVirus)软件，该软件既可以对某个文件夹(Folder)杀毒，也可以对某个指定的文件(File)进行杀毒。该杀毒软件还可以根据各类文件的特点，为不同类型的文件提供不同的杀毒方式，例如图像文件(ImageFile)和文本文件(TextFile)的杀毒方式就有所差异。用户在使用的时候，无论是文件夹还是文件，无论是何种类型的文件，都可以通过同一种操作完成杀毒这个工作。

Component：AbstractFile

```java
public 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();  
}
```

Composite：Folder

```java
public 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 + "'进行杀毒"); 
        //递归调用成员构件的killVirus()方法  
        for(Object obj : fileList) {  
            ((AbstractFile)obj).killVirus();  
        }  
    }  
}
```

Leaf：有2个，分别是`ImageFile ，TextFile`

```java
//图像文件类
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 + "'进行杀毒");  
    }  
}  

//文本文件类
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 + "'进行杀毒");  
    }  
}
```

客户端使用

```java
public class Client {  
    public static void main(String args[]) {  

        AbstractFile file1,file2,file3,file4,file5,folder1,folder2,folder3,folder4;  

        folder = new Folder("my");  
        imageFolder = new Folder("img");  
        textFolder = new Folder("text");  

        folder.add(imageFolder);  
        folder.add(textFolder); 

        iamgeFolder.add(new ImageFile("dog.jpg"));  
        imageFolder.add(new ImageFile("cat.png"));  
        textFolder.add(new TextFile("way-to-architect.txt"));           

        //用户需求1：对目录my进行杀毒
        folder.killVirus();

        //用户需求2：只对图片杀毒
        imageFolder.killVirus();

        //用户需求3：只对way-to-architect.txt文件杀毒
        new TextFile("way-to-architect.txt").killVirus();
    }  
}
```

可以看出，无论用户操作的是树形结构的叶子节点还是容器节点，所执行的操作都是一样的（都是执行`killVirus()`方法）。即用户无须关心节点的层次结构，可以对所选节点进行统一处理。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://maxwell.gitbook.io/way-to-architect/she-ji-mo-shi/jie-gou-xing/zu-he-mo-shi.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
