iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

Composite

The Composite pattern lets you treat single objects and groups of objects the same way. Trees, file systems, scene graphs, UI hierarchies — anywhere a thing might be a leaf or a container of more things.

A file-system tree + a renderable tree

EXAMPLE
// 1) File-system tree — same interface for files and folders
class FsNode {
    constructor(name) { this.name = name; }
    size()      { throw new Error('abstract'); }
    print(prefix = '') { throw new Error('abstract'); }
}

class File extends FsNode {
    constructor(name, bytes) { super(name); this.bytes = bytes; }
    size() { return this.bytes; }
    print(prefix) { console.log(\`${prefix}- ${this.name} (${this.bytes}B)\`); }
}

class Folder extends FsNode {
    constructor(name) { super(name); this.children = []; }
    add(child) { this.children.push(child); return this; }
    size() { return this.children.reduce((s, c) => s + c.size(), 0); }
    print(prefix = '') {
        console.log(\`${prefix}+ ${this.name}/\`);
        for (const c of this.children) c.print(prefix + '  ');
    }
}

const root = new Folder('src')
    .add(new File('main.ts', 1024))
    .add(new Folder('lib')
        .add(new File('util.ts', 512))
        .add(new File('http.ts', 2048)))
    .add(new File('README.md', 256));

console.log('total:', root.size());
root.print();

// 2) Renderable UI tree — every node renders itself + children
class Box {
    constructor() { this.children = []; }
    add(c) { this.children.push(c); return this; }
    render() {
        return \`<div>${this.children.map(c => c.render()).join('')}</div>\`;
    }
}

class Text {
    constructor(s) { this.s = s; }
    render() { return this.s; }
}

const tree = new Box()
    .add(new Text('Header'))
    .add(new Box()
        .add(new Text('left'))
        .add(new Text('right')));

console.log(tree.render());

// 3) Real-world appearances
//   - DOM (HTMLElement has children of HTMLElement)
//   - React components (every component renders more components)
//   - Three.js scene graph (Object3D + Group)
//   - GUI menu systems
//   - JSON / AST / compiler IRs

// 4) Trade-off
// Pro: uniform code, easy recursion.
// Con: leaf operations must implement the full container interface (or stub it).

Why it matters

When you find yourself writing “if (item is a folder) { recurse }” everywhere, that’s Composite begging to be extracted. Make leaves and containers share an interface; recursion happens naturally.

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
// Treat a single object and a group of objects uniformly.
class Component { render() {} }
class Group extends Component {
    constructor() { super(); this.children = []; }
    add(c) { this.children.push(c); }
    render() { return this.children.map(c => c.render()).join(''); }
}
Try it Yourself »

Discussion

Loading…