从零开始学设计模式(十一):组合模式(Composite Pattern):

760 阅读5分钟

这是我参与11月更文挑战的第18天,活动详情查看:2021最后一次更文挑战

作者的其他平台:

| CSDN:blog.csdn.net/qq\_4115394…

| 掘金:juejin.cn/user/651387…

| 知乎:www.zhihu.com/people/1024…

| GitHub:github.com/JiangXia-10…

| 公众号:1024笔记

本文大概3355字,读完共需10分钟

定义:

组合模式(Composite Pattern)又叫做部分-整体模式,它在树型结构(可以想象一下数据结构中的树)的问题中,模糊了简单元素和复杂元素的概念,客户端程序可以像处理简单元素一样来处理复杂元素,而使得客户端程序与复杂元素的内部结构进行解藕。

组合模式一般用来描述整体与部分的关系,它将对象组织到树形结构中,顶层的节点被称为根节点,根节点下面可以包含树枝节点和叶子节点,树枝节点下面又可以包含树枝节点和叶子节点。

如下图的工程目录结构就是一个树型结构:

它可以表示为下面的树型结构图:

由上面的结构图可以发现,根节点和树枝节点其实在本质上属于同一种数据类型,它们都可以作为容器使用;但是叶子节点与树枝节点其实在语义上是不属于同一种类型。但是在组合模式中,是把树枝节点和叶子节点看作属于同一种数据类型(这里是用统一接口定义),从而使得让它们具备一致行为。
所以在组合模式中,整个的树形结构中的对象就会都属于同一种类型,这样的好处就是不需要用户来辨别是树枝节点还是叶子节点,就可以直接进行操作,给用户的使用带来极大的便利。

组成部分:

通过上述对于组合模式的描述,可以总结出组合模式的几个组成部分:
1、Component: 组合中的对象声明接口,在适当的情况下,实现所有类共有接口的默认行为。声明一个接口用于访问和管理Component子部件,不管是组合还是叶结点。
2、Leaf: 在组合中表示叶子结点对象,叶子结点没有子结点。

3、Composite: 容器对象,表示参加组合的有子对象的对象,定义有枝节点行为,用来存储子部件,在Component接口中实现与子部件有关操作,如增加和删除等。

栗子

用上面文件夹的例子,首先声明一个文件夹相关的Component,FolderComponent:

public abstract class FolderComponent {

    private String name;

    public String getName() {
        return name;
    }

    public void setName(final String name) {
        this.name = name;
    }

    public FolderComponent() {
    }

    public FolderComponent(final String name) {
        this.name = name;
    }

    public abstract void add(FolderComponent component);

    public abstract void remove(FolderComponent component);

    public abstract void display();
}

再声明一个叶子节点文件夹,定义为FileLeaf,并且继承上面的FolderComponent:

public class FileLeaf  extends FolderComponent{
    public FileLeaf(final String name) {
        super(name);
    }

    @Override
    public void add(FolderComponent component) {
    }

    @Override
    public void remove(FolderComponent component) {
    }

    @Override
    public void display() {
        System.out.println("叶子文件:" + this.getName());
    }
}

最后声明一个容器对象,FolderComposit

public class FolderComposite extends FolderComponent {
    private final List<FolderComponent> components;

    public FolderComposite(final String name) {
        super(name);
        this.components = new ArrayList<FolderComponent>();
    }

    public FolderComposite() {
        this.components = new ArrayList<FolderComponent>();
    }

    @Override
    public void add(final FolderComponent component) {
        this.components.add(component);
    }

    @Override
    public void remove(final FolderComponent component) {
        this.components.remove(component);
    }

    @Override
    public void display() {
        System.out.println("文件夹组合容器的名字是" + this.getName());
        for (final FolderComponent component : components) {
            System.out.println("文件夹组合容器的当前文件夹是" + component.getName());
        }
    }
}

测试方法:

public class CompositePatternTest {
    public static void main(final String[] args)    {
        final FolderComponent leaf = new FileLeaf("叶子文件");
        leaf.display();

        final FolderComponent folder = new FolderComposite("文件夹一");
        folder.add(new FileLeaf("文件夹里面的文件二"));
        folder.add(new FileLeaf("文件夹里面的文件三"));
        folder.display();
    }
}

运行结果如下:

通过上面的例子可以总结出组合模式的优点和缺点。

组合模式的优点

1、组合模式使得客户端代码可以一致地处理单个对象和组合对象,从而无须关心自己处理的是单个对象,还是组合对象,大大地简化了代码结构;

2、组合模式的使用使得更容易在组合体内加入新的对象,客户端不会因为加入了新的对象而更改源代码,实现了设计模式原则的“开闭原则”;

组合模式的缺点

1、使用组合模式需要花较多地时间理清类之间的层次关系,所以设计较复杂;

2、组合模式理解和设计较为复杂,增加代码难度;

3、如果使用了组合模式就不容易限制容器中的构件;

4、组合模式中不容易用继承的方法来增加构件的新功能;

使用场景

组合模式适用于以下情况:

1、当想要表示对象的部分-整体层次结构

2、当想要用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象。

总结

使用组合模式方便解耦客户端与复杂元素的内部结构,从而使得客户端程序可以像处理简单元素一样来一致处理复杂元素,大大简化了代码。对于层次结构的系统对象,可以使用组合模式进行处理。

相关推荐:

从零开始学设计模式(一):什么是设计模式

从零开始学设计模式(二):单例模式

从零开始学设计模式(三):原型模式(Prototype Pattern)

从零开始学设计模式(四):工厂模式(Factory Pattern)

从零开始学设计模式(五):建造者模式(Builder Pattern)

从零开始学设计模式(六):适配器模式(Adapter Pattern)

从零开始学设计模式(六):代理模式(Proxy Pattern)

从零开始学设计模式(八):装饰器模式(Decorator Pattern)

从零开始学设计模式(九):外观模式(Facade Pattern)

从零开始学设计模式(十):桥接模式(Bridge Pattern)