Saul's blog Saul's blog
首页
后端
分布式
前端
更多
分类
标签
归档
友情链接
关于
GitHub (opens new window)

Saul.J.Wu

立身之本,不在高低。
首页
后端
分布式
前端
更多
分类
标签
归档
友情链接
关于
GitHub (opens new window)
  • Java入门基础

  • Java核心基础

  • 设计模式

    • 概念
    • 创建型模式

    • 结构型模式

      • 适配器
      • 桥接
      • 组合
        • 概念
        • 例子
        • 小结
      • 装饰器
      • 外观
      • 享元
      • 代理
    • 行为型模式

  • Web开发

  • SpringBoot

  • 微服务

  • Elasticsearch

  • 运维

  • 后端
  • 设计模式
  • 结构型模式
SaulJWu
2021-02-10

组合

# 概念

将对象组合成树形结构以表示“部分-整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性。

组合模式(Composite)经常用于树形结构,为了简化代码,使用Composite可以把一个叶子节点与一个父节点统一起来处理。

# 例子

我们来看一个具体的例子。在XML或HTML中,从根节点开始,每个节点都可能包含任意个其他节点,这些层层嵌套的节点就构成了一颗树。

要以树的结构表示XML,我们可以先抽象出节点类型Node:

public interface Node {
    // 添加一个节点为子节点:
    Node add(Node node);
    // 获取子节点:
    List<Node> children();
    // 输出为XML:
    String toXml();
}
1
2
3
4
5
6
7
8

对于一个<abc>这样的节点,我们称之为ElementNode,它可以作为容器包含多个子节点:

public class ElementNode implements Node {
    private String name;
    private List<Node> list = new ArrayList<>();

    public ElementNode(String name) {
        this.name = name;
    }

    public Node add(Node node) {
        list.add(node);
        return this;
    }

    public List<Node> children() {
        return list;
    }

    public String toXml() {
        String start = "<" + name + ">\n";
        String end = "</" + name + ">\n";
        StringJoiner sj = new StringJoiner("", start, end);
        list.forEach(node -> {
            sj.add(node.toXml() + "\n");
        });
        return sj.toString();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

对于普通文本,我们把它看作TextNode,它没有子节点:

public class TextNode implements Node {
	private String text;

	public TextNode(String text) {
		this.text = text;
	}

	public Node add(Node node) {
		throw new UnsupportedOperationException();
	}

	public List<Node> children() {
		return List.of();
	}

	public String toXml() {
		return text;
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

此外,还可以有注释节点:

public class CommentNode implements Node {
	private String text;

	public CommentNode(String text) {
		this.text = text;
	}

	public Node add(Node node) {
		throw new UnsupportedOperationException();
	}

	public List<Node> children() {
		return List.of();
	}

	public String toXml() {
		return "<!-- " + text + " -->";
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

通过ElementNode、TextNode和CommentNode,我们就可以构造出一颗树:

Node root = new ElementNode("school");
root.add(new ElementNode("classA")
        .add(new TextNode("Tom"))
        .add(new TextNode("Alice")));
root.add(new ElementNode("classB")
        .add(new TextNode("Bob"))
        .add(new TextNode("Grace"))
        .add(new CommentNode("comment...")));
System.out.println(root.toXml());
1
2
3
4
5
6
7
8
9

最后通过root节点输出的XML如下:

<school>
<classA>
Tom
Alice
</classA>
<classB>
Bob
Grace
<!-- comment... -->
</classB>
</school>
1
2
3
4
5
6
7
8
9
10
11

可见,使用Composite模式时,需要先统一单个节点以及“容器”节点的接口:

             ┌───────────┐
             │   Node    │
             └───────────┘
                   ▲
      ┌────────────┼────────────┐
      │            │            │
┌───────────┐┌───────────┐┌───────────┐
│ElementNode││ TextNode  ││CommentNode│
└───────────┘└───────────┘└───────────┘
1
2
3
4
5
6
7
8
9

作为容器节点的ElementNode又可以添加任意个Node,这样就可以构成层级结构。

类似的,像文件夹和文件、GUI窗口的各种组件,都符合Composite模式的定义,因为它们的结构天生就是层级结构。

# 小结

Composite模式使得叶子对象和容器对象具有一致性,从而形成统一的树形结构,并用一致的方式去处理它们。

帮我改善此页面 (opens new window)
上次更新: 2021/02/16, 02:24:29
桥接
装饰器

← 桥接 装饰器→

最近更新
01
zabbix学习笔记二
02-28
02
zabbix学习笔记一
02-10
03
Linux访问不了github
12-08
更多文章>
Theme by Vdoing | Copyright © 2020-2022 Saul.J.Wu | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式