Treat a single item and a group of items through the same interface, so client code doesn't need to know which it has.
Calculating a folder's total size means handling files and folders differently everywhere you touch them — a file has a size property, a folder has to sum its children, and folders can contain folders. Every caller ends up re-implementing that branch.
Both `File` (leaf) and `Folder` (composite) implement the same method, e.g. `getSize()`. A folder's implementation just calls `getSize()` on each child and sums the results — including child folders, recursively. Callers never check which kind of node they have.
`getSize()` works identically whether it's called on a single file or on the folder at the very top of the tree.
class File {
constructor(name, size) { this.name = name; this.size = size; }
getSize() { return this.size; }
}
class Folder {
constructor(name) { this.name = name; this.children = []; }
add(node) { this.children.push(node); return this; }
getSize() {
return this.children.reduce((sum, child) => sum + child.getSize(), 0);
}
}
const src = new Folder("src")
.add(new File("index.js", 2_000))
.add(new File("utils.js", 800));
const project = new Folder("project")
.add(src)
.add(new File("README.md", 400));
console.log(project.getSize()); // 3200 — files + nested folder, uniformly