SwadhyAI Notes

All Low-Level Design notes

Structural · recursive part-whole

Composite

is a structural pattern that lets a single item and a group of items share one interface. Clients call the same method on the root whether that root is a leaf or a nested tree.

A concrete example: a folder’s size is not a number you typed by hand. It is the sum of the sizes of the files inside, and some of those “files” are folders too. You want one question — size() — without asking “are you a file?” at every level.

Explorer — one type, nested ownership

docs/size() → 48
readme.md12
api/size() → 36
openapi.yaml20
errors.md16

Client calls docs.size(). The folder sums children. No instanceof in the caller.

Core question

Can a leaf and a group share one type so clients recurse without instanceof?

Name the . Give leaves and groups that . Store children only on the group. Recurse inside the group. The client asks the root.

Three roles, one contract

Component

Node.size()

What every caller holds. Honest on a file and on a folder.

FileNode

Returns its own bytes. No list. No add().

Composite

Folder

List of Node. add() lives here. size() sums. This is the owner.

Java sketch: File and Folder share Node

Keep add on Folder. Clients that only have Node can still ask size(); they should not grow the tree. That choice respects Interface Segregation better than File.add throwing UnsupportedOperationException.

java
interface Node {
  int size();
}

class FileNode implements Node {
  private final int bytes;
  FileNode(int bytes) { this.bytes = bytes; }
  public int size() { return bytes; }
}

class Folder implements Node {
  private final List<Node> children = new ArrayList<>();

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

  public int size() {
    int total = 0;
    for (Node child : children) total += child.size();
    return total;
  }
}

// Node root = docs;  int used = root.size();

Same tree: rules that contain rules

A rule engine is Composite with a boolean verb. AmountAtLeast is a leaf. AllOf / AnyOf are groups. The checkout asks root.matches(order) and does not switch on rule kinds. See also Rule engine.

java
interface Evaluable {
  boolean matches(Order order);
}

class AmountAtLeast implements Evaluable {
  private final Money min;
  public boolean matches(Order order) { return order.total().gte(min); }
}

class AllOf implements Evaluable {
  private final List<Evaluable> rules;
  public boolean matches(Order order) {
    for (Evaluable rule : rules) {
      if (!rule.matches(order)) return false;
    }
    return true;
  }
}

Org chart, UI widgets, in-memory FS

Employee and Manager both report headcount. A panel of widgets is a widget. An in-memory file system is this page in a case study: Directory owns children; File holds bytes; both are Node.

add() only on the group

GoF sometimes puts add on Component and makes leaves throw. That surprises callers and fights ISP. Prefer add on Folder. If a client has only Node, it can still ask size().

Not a list, not a visitor, not a wrap

If you notice…You probably want
A flat List<File> and no nested foldersA collection. Composite needs recursion.
Many unrelated operations over the same treeVisitor on top of Composite
instanceof Folder in the client to walkThe pattern is failing in public. Push the walk into size().
A wrapper that changes Stripe into PayableAdapter, not a tree

How to implement it (four steps)

  1. 01

    Name the common verb

    size, evaluate, draw, headcount — honest on a leaf and on a group.

  2. 02

    Give leaves and groups that type

    File and Folder implement Node. Rule and RuleGroup implement Evaluable.

  3. 03

    Store children only on the group

    Folder has List<Node>. File does not.

  4. 04

    Recurse inside the group

    The client calls root.size(). The tree does the walk.

Common mistakes

instanceof in the client

if (node instanceof Folder) walk((Folder) node) is the pattern failing. size() and evaluate() already know how to walk.

add() on every leaf

File.add(child) that throws, or silently no-ops, surprises callers. Keep add on Folder.

One file in two folders

Unless you mean a link, ownership is unique. Deleting one parent must not surprise the other. Model a link type if you need sharing.

How to explain this in an interview

“File and Folder both implement Node. size() on a folder sums children. The client never asks instanceof. add lives on Folder so leaves stay honest.”
  1. Draw docs/ with two files and one nested folder. Show which objects implement Node and where add lives.
  2. AND/OR rule groups: write matches() without a type switch in the caller.

Takeaway

Composite is a tree of one contract. Leaves answer the verb. Groups recurse. Clients never walk with instanceof. That is the whole pattern.