Skip to content
On this page

Composite

Compose objects into tree structures to represent part-whole hierarchies, allowing users to treat individual objects and compositions uniformly.

The Composite pattern is often used in tree structures, simplifying code by allowing a leaf node and its parent node to be processed uniformly.

Let's look at a specific example. In XML or HTML, starting from the root node, each node may contain any number of other nodes, creating a nested tree structure.

To represent XML in a tree structure, we can abstract a node type called Node:

java
public interface Node {
    // Add a node as a child node:
    Node add(Node node);
    // Get child nodes:
    List<Node> children();
    // Output as XML:
    String toXml();
}

For a node like <abc>, we call it an ElementNode, which can act as a container for multiple child nodes:

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

For plain text, we treat it as a TextNode, which has no child nodes:

java
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;
    }
}

Additionally, we can have a comment node:

java
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 + " -->";
    }
}

By using ElementNode, TextNode, and CommentNode, we can construct a tree:

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

The XML output from the root node is as follows:

xml
<school>
    <classA>
        Tom
        Alice
    </classA>
    <classB>
        Bob
        Grace
        <!-- comment... -->
    </classB>
</school>

As you can see, when using the Composite pattern, we need to unify the interfaces of individual nodes and "container" nodes:

             ┌───────────┐
             │   Node    │
             └───────────┘

      ┌────────────┼────────────┐
      │            │            │
┌───────────┐┌───────────┐┌───────────┐
│ElementNode││ TextNode  ││CommentNode│
└───────────┘└───────────┘└───────────┘

The ElementNode, as a container node, can add any number of Node instances, allowing the construction of a hierarchical structure.

Similarly, structures like folders and files, or various components of GUI windows, conform to the definition of the Composite pattern, as they are inherently hierarchical.

Exercise

Use the Composite pattern to construct XML.

Summary

The Composite pattern enables leaf and container objects to be treated uniformly, forming a unified tree structure that can be handled in a consistent manner.

Composite has loaded