Skip to content

Composite

Composite is a design pattern that can be used if we are able to represent objects as trees. Such a tree consists of branches that are a single object from the user's perspective.

In order to use the composite, we need the following elements:

  • common interface or base class for objects in a tree is often called a component.
  • a class representing a single object on a branch (which is a component). Such an object does not contain any children, often called a 'leaf' (from which other branches will grow).
  • a composite class, also a component, containing a set of many leaves.

The client works on a composite that has the ability to manage all objects in the data structure.

composite

Example

The following example shows a simple implementation that consists of the following parts:

  • Line, the interface representing the component
  • DottedLine andSolidLine, which are implementations of the Line interface (they are leaf objects)
  • CompoundLine which is a composite, i.e. a class that groups multiple Line objects
  • LineEditor, which shows a simple composite client.
import lombok.RequiredArgsConstructor;
import lombok.Value;

@Value
@RequiredArgsConstructor
public class Point {
  private final int x;
  private final int y;
}
public interface Line {
  void draw(double lengthInPixels);
  void setStartingPosition(Point position);
  Point getStartingPoint();
}
public class DottedLine implements Line {

  private Point point = new Point(10, 10);

  @Override
  public void draw(final double lengthInPixels) {
    System.out.println("Drawing d.o.t.t.e.d line starting in (" + point.getX() + ", " + point.getY() + ") with length " + lengthInPixels);
  }

  @Override
  public void setStartingPosition(final Point position) {
    this.point = position;
  }

  @Override
  public Point getStartingPoint() {
    return point;
  }
}
public class SolidLine implements Line {

  private Point point = new Point(0, 0);

  @Override
  public void draw(final double lengthInPixels) {
    System.out.println("Drawing solid line starting in (" + point.getX() + ", " + point.getY() + ") with length " + lengthInPixels);
  }

  @Override
  public void setStartingPosition(final Point position) {
    this.point = position;
  }

  @Override
  public Point getStartingPoint() {
    return point;
  }
}
import java.util.ArrayList;
import java.util.List;

// this class can draw lines in a 2d space
public class CompoundLine implements Line {

  private final List<Line> lines = new ArrayList<>();

  @Override
  public void draw(final double lengthInPixels) {
    lines.forEach(line -> line.draw(lengthInPixels));
  }

  @Override
  public void setStartingPosition(final Point position) {
    lines.forEach(line -> line.setStartingPosition(position));
  }

  @Override
  public Point getStartingPoint() {
    if (lines.isEmpty()) {
      return new Point(0, 0);
    }
    return lines.get(0).getStartingPoint();
  }

  public void addLine(final Line line) {
    lines.add(line);
  }

  public void removeLine(final Line line) {
    lines.remove(line);
  }
}
import java.util.List;

public abstract class LineEditor {

  private final CompoundLine compoundLine = new CompoundLine();

  public void selectAndRemove(final List<Line> lines) {
    lines.forEach(compoundLine::removeLine);
  }

  public void createLine(final String type) {
    if (type.equals("dotted")) {
      compoundLine.addLine(new DottedLine());
    } else {
      compoundLine.addLine(new SolidLine());
    }
  }

  public void drawShortAllAtPoint(final Point point) {
    compoundLine.setStartingPosition(point);
    compoundLine.draw(5.0);
  }
}

In conclusion, the Composite is a helpful pattern if you want to give the end-user the possibility to use one common interface that can represent a single object, as well as its group (that can be presented as a tree structure).

The main challenge when trying to use this pattern is finding a common interface for a component object. However when this is done, it will likely make it much easier for us to navigate through complicated data structures (or we can combine the implementation with the pattern iterator).