Card 03/ 08

CodeDifficulty: Intermediate3 min

Composite Template in Python, Java and TypeScript

One leaf, one composite, and an operation that recurses through a small tree.

The Composite template is the pattern in its pure form, with the participant names from the class diagram and no domain. Each leaf's operation returns its name, and each composite's returns its own name with its children's results in brackets, so the output shows the shape of the tree. The steps add each participant in Python, and the tabs give the complete program in three languages, each with its output.

Building the template one participant at a time

Component

The interface every node shares.

py
class Component(ABC):
    @abstractmethod
    def operation(self) -> str: ...

Leaf

A node that answers for itself.

py
class Leaf(Component):
    def operation(self) -> str:
        return self._name

Composite

A node that holds components and asks each one.

py
class Composite(Component):
    def add(self, child: Component) -> None:
        self._children.append(child)

    def operation(self) -> str:
        inner = ", ".join(child.operation() for child in self._children)
        return f"{self._name}({inner})"

Client

Calls operation on a leaf, a composite, and a composite holding a composite.

py
client(Leaf("A"))
client(branch)
client(root)

The complete template

py
from abc import ABC, abstractmethod


class Component(ABC):
    @abstractmethod
    def operation(self) -> str: ...


class Leaf(Component):
    def __init__(self, name: str) -> None:
        self._name = name

    def operation(self) -> str:
        return self._name


class Composite(Component):
    def __init__(self, name: str) -> None:
        self._name = name
        self._children: list[Component] = []

    def add(self, child: Component) -> None:
        self._children.append(child)

    def operation(self) -> str:
        inner = ", ".join(child.operation() for child in self._children)
        return f"{self._name}({inner})"


def client(component: Component) -> None:
    print(component.operation())


branch = Composite("Branch")
branch.add(Leaf("B"))
branch.add(Leaf("C"))
root = Composite("Root")
root.add(Leaf("A"))
root.add(branch)

client(Leaf("A"))
client(branch)
client(root)
OutputRan with python:3.12-slim (Python 3.12.13)
A
Branch(B, C)
Root(A, Branch(B, C))
`Composite` holds `Component` objects, so `root` can hold `branch`.
java
import java.util.ArrayList;
import java.util.List;

public class CoTemplate {
    static void client(Component component) {
        System.out.println(component.operation());
    }

    public static void main(String[] args) {
        Composite branch = new Composite("Branch");
        branch.add(new Leaf("B"));
        branch.add(new Leaf("C"));
        Composite root = new Composite("Root");
        root.add(new Leaf("A"));
        root.add(branch);

        client(new Leaf("A"));
        client(branch);
        client(root);
    }
}

interface Component {
    String operation();
}

class Leaf implements Component {
    private final String name;

    Leaf(String name) {
        this.name = name;
    }

    public String operation() {
        return name;
    }
}

class Composite implements Component {
    private final String name;
    private final List<Component> children = new ArrayList<>();

    Composite(String name) {
        this.name = name;
    }

    void add(Component child) {
        children.add(child);
    }

    public String operation() {
        List<String> parts = new ArrayList<>();
        for (Component child : children) {
            parts.add(child.operation());
        }
        return name + "(" + String.join(", ", parts) + ")";
    }
}
OutputRan with eclipse-temurin:21-jdk (Java 21.0.12)
A
Branch(B, C)
Root(A, Branch(B, C))
Java places the client first so the source launcher finds `main`; the roles are the same.
ts
interface Component {
  operation(): string;
}

class Leaf implements Component {
  constructor(private readonly name: string) {}

  operation(): string {
    return this.name;
  }
}

class Composite implements Component {
  private readonly children: Component[] = [];

  constructor(private readonly name: string) {}

  add(child: Component): void {
    this.children.push(child);
  }

  operation(): string {
    const inner = this.children.map((child) => child.operation()).join(", ");
    return `${this.name}(${inner})`;
  }
}

function client(component: Component): void {
  console.log(component.operation());
}

const branch = new Composite("Branch");
branch.add(new Leaf("B"));
branch.add(new Leaf("C"));
const root = new Composite("Root");
root.add(new Leaf("A"));
root.add(branch);

client(new Leaf("A"));
client(branch);
client(root);
OutputRan with node:22-slim (Node 22.23.1), TypeScript 5.9.3
A
Branch(B, C)
Root(A, Branch(B, C))
`map` calls `operation` on each child, whether it is a leaf or a composite.

The three lines of output come from the same client function. The first is a single leaf, the second a composite of two leaves, and the third a composite that holds a leaf and the second composite. Nothing in client or in Composite.operation mentions depth; the recursion follows the tree because each child is asked the same question as its parent.