组合模式是一种结构型设计模式,用于将对象组合成树形结构以表示“部分-整体”的层次结构。它让客户端可以统一对待单个对象和组合对象。

核心思想

  • 统一接口: 定义一个接口,所有组件(无论是叶子还是组合)都实现这个接口。
  • 递归组合: 组合对象可以包含多个子对象,这些子对象也可以是组合对象或叶子对象。

组件类型

  • 叶子节点: 最小的基本对象,没有子对象。
  • 组合节点: 容器对象,可以包含叶子或其他组合。

工作原理

  1. 定义组件接口: 包含所有子类需要实现的方法。
  2. 实现叶子类: 实现组件接口的方法,执行具体操作。
  3. 实现组合类: 包含子组件的列表,实现接口方法,并在这些方法中递归调用子组件的方法。

适用场景

  • 希望客户端能以统一的方式处理单个对象和组合对象。
  • 需要表示对象的部分-整体层次结构。

优点

  • 简化客户端代码,不再需要关心处理的是单个对象还是组合对象。
  • 更容易添加新类型的组件。

缺点

  • 可能使设计更加复杂。
  • 不容易限制组合中的组件类型。

示例

文件系统中,文件和文件夹就是组合模式的典型例子。文件夹可以包含文件或其他文件夹,客户端可以用相同的方式处理文件和文件夹。

应用场景

  • 系统的文件处理系统
  • UI界面的Menu item
  • 公司人员职级划分
  • ...

What is Composite Pattern

Composite Pattern(组合模式)也叫叉数、对象树、Object Tree、...。它的思想很类似自然界的树状结构。组合模式也是用于描述具有层次结构的数据。

树状结构很有意思的地方在于,每个树枝都是一样的,甚至树叶和树枝都是一样的,区别只是在于树叶下面没有树叶了。
组合模式就是借鉴了这个思想,它允许将对象组合成树状结构,并以统一的方式处理对象及对象组合。组合模式使得客户端可以一致地对待单个对象和对象组合,无需区分它们的差异。

关键要素

  • leaf and branch interface
  • leaf
  • branch
  • Client

Example —— 读取文件夹和文件

假设我们要设计一个文件系统的结构,包括文件和文件夹。文件夹可以包含文件和其他文件夹,而文件没有子组件。我们可以使用组合模式来实现这个文件系统结构。

leaf and branch interface

public interface FileSystemComponent {
    void showInfo();
}

leaf and folder

public class File implements FileSystemComponent {
    private String name;
    
    public File(String name) {
        this.name = name;
    }
    
    public void showInfo() {
        System.out.println("File: " + name);
    }
}


public class Folder implements FileSystemComponent {
    private String name;
    private List<FileSystemComponent> components;
    
    public Folder(String name) {
        this.name = name;
        this.components = new ArrayList<>();
    }
    
    public void addComponent(FileSystemComponent component) {
        components.add(component);
    }
    
    public void removeComponent(FileSystemComponent component) {
        components.remove(component);
    }
    
    public void showInfo() {
        System.out.println("Folder: " + name);
        for (FileSystemComponent component : components) {
            component.showInfo();
        }
    }
}

Client

public class Client {
    public static void main(String[] args) {
        // 创建文件和文件夹
        FileSystemComponent file1 = new File("file1.txt");
        FileSystemComponent file2 = new File("file2.doc");
        
        Folder folder1 = new Folder("Folder 1");
        Folder folder2 = new Folder("Folder 2");
        
        // 组合文件和文件夹
        folder1.addComponent(file1);
        folder1.addComponent(file2);
        folder1.addComponent(folder2);
        
        // 显示文件系统结构
        folder1.showInfo();
    }
}

out :

Folder: Folder 1
File: file1.txt
File: file2.doc
Folder: Folder 2

通过组合模式,我们可以将文件和文件夹组织成树状结构,使用统一的方式处理它们。
客户端可以递归地遍历文件系统的组件,无需关心是单个文件还是文件夹。这样,组合模式提供了一种灵活且可扩展的方式来处理对象的组合关系。

Example —— 统计城市人口数

这个案例并不是那么恰当!

interface

public interface Counter {

    int count();
}

leaf

public class Leaf implements Counter {

    private int sum = 0;

    public Leaf(int sum) {
        this.sum = sum;
    }

    @Override
    public int count() {
        return sum;
    }
}

Branch

public class Branch implements Counter {

    private List<Counter> counterList = new ArrayList<>();

    public void add(Counter counter) {
        counterList.add(counter);
    }

    public void delete(Counter counter) {
        counterList.remove(counter);
    }

    public List<Counter> getChild() {
        return counterList;
    }

    @Override
    public int count() {
        int sum = 0;
        for (Counter counter : counterList) {
            sum += counter.count();
        }
        return sum;
    }
}

Client

    /**
     * 需求是做一个统计全国人口数量的功能
     * 全国根据地区划分就是一个树状结构
     * ------------------------------
     * 国家
     *   城市1
     *     区1
     *     区2
     *   城市2
     *     区1
     *     区2
     * ------------------------------
     */
    public static void main(String[] args) {
        System.out.println("开始统计人数");
        Branch city = new Branch(); // 模拟市
        Leaf beijing = new Leaf(100);
        Leaf shanghai = new Leaf(200);
        city.add(beijing);
        city.add(shanghai);

        Branch district = new Branch(); // 模拟区
        Leaf dongcheng = new Leaf(300);
        Leaf xicheng = new Leaf(400);
        district.add(dongcheng);
        district.add(xicheng);

        city.add(district);  // 区加入城市
        System.out.println(city.count()); //1000
    }
posted on 2023-08-30 15:52  Mysticbinary  阅读(18)  评论(0编辑  收藏  举报