Composite Pattern: Hierarchical Structural Modeling

The Composite pattern treats individual objects and compositions of objects uniformly. It is the architectural foundation for tree structures, allowing clients to ignore the difference between a "Leaf" (terminal node) and a "Composite" (container node).

1. Concrete Implementation: File System Explorer

In a file system, both `File` and `Directory` share a common interface for operations like `getSize()` or `list()`.

1.1 The Component Interface

```java

import java.util.List;

public interface FileSystemNode {

String getName();

long getSize();

void print(String indent);

}

```

1.2 The Leaf (File)

```java

public record File(String name, long size) implements FileSystemNode {

@Override

public String getName() { return name; }

@Override

public long getSize() { return size; }

@Override

public void print(String indent) {

System.out.println(indent + "📄 " + name + " (" + size + " bytes)");

}

}

```

1.3 The Composite (Directory)

The Composite node manages a collection of components and recursively delegates operations.

```java

import java.util.ArrayList;

import java.util.List;

public class Directory implements FileSystemNode {

private final String name;

private final List<FileSystemNode> children = new ArrayList<>();

public Directory(String name) {

this.name = name;

}

public void add(FileSystemNode node) {

children.add(node);

}

@Override

public String getName() { return name; }

@Override

public long getSize() {

return children.stream()

.mapToLong(FileSystemNode::getSize)

.sum();

}

@Override

public void print(String indent) {

System.out.println(indent + "📁 " + name);

for (FileSystemNode child : children) {

child.print(indent + " ");

}

}

}

```

2. Practitioner Insights

2.1 The Transparency vs. Safety Trade-off

* **Transparency:** Define `add()`/`remove()` in the base interface. This allows clients to treat all nodes identically but forces Leaf nodes to throw `UnsupportedOperationException`.

* **Safety:** Define management methods only in the `Composite` class. This prevents runtime errors on Leaves but requires clients to cast or use type checking before modifying the tree.

2.2 Recursion Depth

For extremely deep trees, recursive implementation of `getSize()` can lead to `StackOverflowError`. In such cases, use an iterative traversal with an explicit `java.util.Deque` as a stack.

2.3 Caching Results

In large, static trees, the overhead of calculating `getSize()` recursively can be significant. Implementing a "dirty" flag or caching the size at the Composite level improves performance for read-heavy workloads.

2.4 Synergy with Visitor

If you need to add many new operations (e.g., `compress()`, `calculateChecksum()`), use the [Visitor Pattern](VisitorPattern) to avoid polluting the `FileSystemNode` interface and breaking the Open/Closed Principle.