Das Kompositum (engl. Composite) ist ein Strukturmuster, das angewendet wird, um Objekte, die sich ein gemeinsames Interface teilen, zu einer hierarchischer Baumstruktur zusammenfügen. Die grundlegende Idee des Kompositum-Musters ist es, sowohl primitive Objekte als auch zusammengesetzte Objekte, die aus mehreren primitiven Objekte bestehen, in einem Interface zu repräsentieren. Dadurch können einzelne Objekte und ihre Kompositionen einheitlich behandelt werden.

Es soll also folgende Frage durch das Muster beantwortet werden: Wie können in einer objektorientierten Sprache einzelne Objekte als Teile (= Komponenten) geschickt zu einem Ganzen (= Kompositum) zusammengesetzt werden?

Die Herleitung des Musters soll wieder über ein Beispiel erfolgen. Es liegt die folgende Implementierung vor, um die 3 grundlegenden Formen Line, Circle und Text grafisch darzustellen. Aus diesen primitiven Formen sollen komplexe, zusammengesetzte Formen erzeugt werden können.

class Line {
    GraphicsContext gc;
    Point2D a, b;
    void draw() { gc.strokeLine(a.getX(), a.getY(), b.getX(), b.getY()); }
}
class Circle {
    GraphicsContext gc;
    Point2D a;
    Double radius;
    void draw() { gc.strokeOval(a.getX(), a.getY(), radius, radius); }
}
class Text {
    GraphicsContext gc;
    Point2D a;
    String value;
    void draw() { gc.fillText(value, a.getX(), a.getY()); }
}

Es bietet sich an, ein gemeinsames Interface für die Formen zu spezifieren, das die Methode draw für jede Form vorschreibt. Da sich alle Formen auch ein gemeinsames Attribut vom Typ GraphicsContext teilen und ein Interface zustandslos ist, wird eine abstrakte Klasse Shape eingeführt.

abstract class Shape {
    GraphicsContext gc;
    abstract void draw();
}
class Line extends Shape {
    Point2D a, b;

    @Override
    void draw() { gc.strokeLine(a.getX(), a.getY(), b.getX(), b.getY()); }
}
class Circle extends Shape {
    Point2D a;
    Double radius;

    @Override
    void draw() { gc.strokeOval(a.getX(), a.getY(), radius, radius); }
}
class Text extends Shape {
    Point2D a;
    String value;

    @Override
    void draw() { gc.strokeText(value, a.getX(), a.getY()); }
}

Das eigentliche Kompositum ist eine zusammengesetzte Form, die ihrerseits aus den anderen bisher zur Verfügung stehenden Formen besteht.

class CompositeShape extends Shape {
    Set<Shape> components = new HashSet<>();
	
    @Override
    void draw() { components.forEach(c -> c.draw()); }
}

Beispiele für zusammengesetzte Formen sind das folgend dargestellte Rechteck, das aus 4 Linien besteht, und das Strichmännchen, das aus einem Kreis und 4 Linien besteht.

Dadurch dass das Kompositum als Teile nicht nur primitive Formen sondern auch andere zusammengesetzte Formen aufnehmen kann, entsteht eine Teil-Ganzes-Hierarchie, die in ihrer Tiefe grundsätzlich nicht beschränkt ist. Die Beziehungen der beteiligten Objekte stellen eine Baumstruktur dar, da zyklische Beziehungen i.d.R. nicht erwünscht sind und daher beim Einfügen von Teilen zu einem Ganzen verhindert werden sollten. Da es sich um eine Baumstruktur handelt, nennen wir die Basisformen auch Blätter (engl. Leaf). Das folgende Objektdiagramm verdeutlicht eine derartige verschachtelte Baumstruktur, die die obigen Formen weiterverwendet. Die resultierende komplexe Form ist daneben dargestellt. Auf Basis weniger Basisformen können so beliebig komplexe Kompositionen zusammengestellt werden.

Das Entwurfsmuster Kompositum ist nun eine Verallgemeinerung des obigen Beispiels für die 2-dimensionalen Formen, d.h. die zu implementierende Methode heißt allgemein anyOperation anstatt draw, das gemeinsame Interface heißt Component anstatt Shape, usw. Der Klasse Kompositum werden noch Methoden hinzugefügt, um eine Komponente aufzunehmen bzw. zu entfernen. Das folgende UML-Klassendiagramm des Kompositum-Musters entspricht dem darunter stehenden Code.

interface Component {
    void anyOperation();
}

class Leaf implements Component {
    @Override
    void anyOperation() { /* ... */ }
}

class Composite implements Component {
    Set<Component> components = new HashSet<>();
	
    @Override
    void anyOperation() { components.forEach(c -> c.anyOperation()); }
	
	void addComponent(Component c) { components.add(c); }
	void removeComponent(Component c) { components.remove(c); }
}