Files
freemo d9e5668cec fix(skills): comprehensive final audit pass for programming-patterns skill
Fixes and improvements from exhaustive audit:

Consistency fixes in SKILL.md:
- 'Pipe & Filter' → 'Pipe and Filter' (one stray '&' found and corrected)
- 'Singleton for factory instance' → clarified to 'register factory as
  singleton-scoped via DI container' (less misleading wording)
- Documentation Format section updated with note that SKILL.md itself is the
  authoritative source for related-pattern combinations

Coverage fix — Related Patterns sections:
- Added '## Related Patterns' to ALL 94 pattern files (was 0/94)
- Each section lists 3–6 related patterns with relationship descriptions
- Covers: why they're related, when to prefer one vs the other,
  and which are often confused

SOLID principles → Creational → Structural → Behavioral → Architectural →
Concurrency → Functional → Resilience → Data Access → Messaging →
Testing → Error Handling → Microservice — all 13 categories covered

Code verification:
- Python: 0 failures (all 85 testable blocks pass)
- Go: 0 failures (all 76 testable blocks pass)
- JavaScript: 0 failures (all 78 testable blocks pass)
- All 239 code blocks verified correct after edits

Final skill state:
- 108 files, 36,524 lines across 13 reference categories
- 94/94 pattern files have Related Patterns sections
- 2,815-line SKILL.md with 67 decision trees, 23 scenarios,
  0 broken references, 0 naming inconsistencies
2026-04-15 13:22:13 -04:00

8.2 KiB

Composite Pattern

Compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions uniformly.

Problem

You have a file system with files and directories. A directory can contain files and other directories. You need to calculate the total size of any node — whether it's a single file or an entire directory tree — using the same interface.

Solution

Define a common Component interface with a size() method. File is a leaf that returns its own size. Directory is a composite that sums the sizes of its children.

        ┌──────────────────┐
        │ Component (intf) │
        │ + size(): int    │
        │ + name(): string │
        └──────┬───────────┘
               │
       ┌───────┴───────┐
       │               │
  ┌────┴────┐   ┌──────┴──────┐
  │  File   │   │  Directory  │
  │ (leaf)  │   │ (composite) │
  └─────────┘   │ + children  │
                │ + add()     │
                └─────────────┘

When to Use

  • You need to represent part-whole hierarchies (trees).
  • You want clients to treat individual objects and compositions uniformly.
  • You want to add new leaf or composite types without changing client code.

When to Avoid

  • The structure is flat with no nesting — a simple list suffices.
  • Leaf and composite operations differ significantly, making a uniform interface forced.
  • You need strict type safety between leaf and composite at compile time.

Pseudocode

interface Component:
    method size(): int
    method name(): string

class File implements Component:
    field _name: string
    field _size: int

    constructor(name, size):
        this._name = name
        this._size = size

    method size(): return _size
    method name(): return _name

class Directory implements Component:
    field _name: string
    field children: list<Component>

    constructor(name):
        this._name = name
        this.children = []

    method add(child: Component):
        children.append(child)

    method size():
        total = 0
        for child in children:
            total += child.size()
        return total

    method name(): return _name

// Usage
root = Directory("root")
root.add(File("readme.md", 100))
src = Directory("src")
src.add(File("main.py", 500))
src.add(File("utils.py", 300))
root.add(src)
print root.name() + " total size: " + root.size()

Python

from abc import ABC, abstractmethod


class Component(ABC):
    """Common interface for files and directories."""

    @abstractmethod
    def size(self) -> int: ...

    @abstractmethod
    def name(self) -> str: ...

    def display(self, indent: int = 0) -> None:
        print(f"{'  ' * indent}{self.name()} ({self.size()} bytes)")


class File(Component):
    """Leaf node."""

    def __init__(self, name: str, size: int) -> None:
        self._name = name
        self._size = size

    def size(self) -> int:
        return self._size

    def name(self) -> str:
        return self._name


class Directory(Component):
    """Composite node that contains children."""

    def __init__(self, name: str) -> None:
        self._name = name
        self._children: list[Component] = []

    def add(self, child: Component) -> "Directory":
        self._children.append(child)
        return self

    def size(self) -> int:
        return sum(child.size() for child in self._children)

    def name(self) -> str:
        return self._name

    def display(self, indent: int = 0) -> None:
        print(f"{'  ' * indent}{self.name()}/ ({self.size()} bytes)")
        for child in self._children:
            child.display(indent + 1)


if __name__ == "__main__":
    root = Directory("root")
    root.add(File("readme.md", 100))
    root.add(File(".gitignore", 50))

    src = Directory("src")
    src.add(File("main.py", 500))
    src.add(File("utils.py", 300))

    tests = Directory("tests")
    tests.add(File("test_main.py", 400))

    src.add(tests)
    root.add(src)

    root.display()
    print(f"\nTotal size: {root.size()} bytes")

Output:

root/ (1350 bytes)
  readme.md (100 bytes)
  .gitignore (50 bytes)
  src/ (1200 bytes)
    main.py (500 bytes)
    utils.py (300 bytes)
    tests/ (400 bytes)
      test_main.py (400 bytes)

Total size: 1350 bytes

Go

package main

import (
	"fmt"
	"strings"
)

// Component is the common interface.
type Component interface {
	Size() int
	Name() string
	Display(indent int)
}

// File is a leaf node.
type File struct {
	name string
	size int
}

func NewFile(name string, size int) *File {
	return &File{name: name, size: size}
}

func (f *File) Size() int    { return f.size }
func (f *File) Name() string { return f.name }
func (f *File) Display(indent int) {
	fmt.Printf("%s%s (%d bytes)\n", strings.Repeat("  ", indent), f.name, f.size)
}

// Directory is a composite node.
type Directory struct {
	name     string
	children []Component
}

func NewDirectory(name string) *Directory {
	return &Directory{name: name}
}

func (d *Directory) Add(c Component) *Directory {
	d.children = append(d.children, c)
	return d
}

func (d *Directory) Size() int {
	total := 0
	for _, child := range d.children {
		total += child.Size()
	}
	return total
}

func (d *Directory) Name() string { return d.name }

func (d *Directory) Display(indent int) {
	fmt.Printf("%s%s/ (%d bytes)\n", strings.Repeat("  ", indent), d.name, d.Size())
	for _, child := range d.children {
		child.Display(indent + 1)
	}
}

func main() {
	root := NewDirectory("root")
	root.Add(NewFile("readme.md", 100))
	root.Add(NewFile(".gitignore", 50))

	src := NewDirectory("src")
	src.Add(NewFile("main.py", 500))
	src.Add(NewFile("utils.py", 300))

	tests := NewDirectory("tests")
	tests.Add(NewFile("test_main.py", 400))
	src.Add(tests)

	root.Add(src)

	root.Display(0)
	fmt.Printf("\nTotal size: %d bytes\n", root.Size())
}

Output:

root/ (1350 bytes)
  readme.md (100 bytes)
  .gitignore (50 bytes)
  src/ (1200 bytes)
    main.py (500 bytes)
    utils.py (300 bytes)
    tests/ (400 bytes)
      test_main.py (400 bytes)

Total size: 1350 bytes

JavaScript

class File {
  constructor(name, size) {
    this._name = name;
    this._size = size;
  }

  size() {
    return this._size;
  }
  name() {
    return this._name;
  }

  display(indent = 0) {
    console.log(`${"  ".repeat(indent)}${this.name()} (${this.size()} bytes)`);
  }
}

class Directory {
  constructor(name) {
    this._name = name;
    this._children = [];
  }

  add(child) {
    this._children.push(child);
    return this;
  }

  size() {
    return this._children.reduce((sum, child) => sum + child.size(), 0);
  }

  name() {
    return this._name;
  }

  display(indent = 0) {
    console.log(
      `${"  ".repeat(indent)}${this.name()}/ (${this.size()} bytes)`
    );
    for (const child of this._children) {
      child.display(indent + 1);
    }
  }
}

// --- Main ---
const root = new Directory("root");
root.add(new File("readme.md", 100));
root.add(new File(".gitignore", 50));

const src = new Directory("src");
src.add(new File("main.py", 500));
src.add(new File("utils.py", 300));

const tests = new Directory("tests");
tests.add(new File("test_main.py", 400));
src.add(tests);

root.add(src);

root.display();
console.log(`\nTotal size: ${root.size()} bytes`);

Output:

root/ (1350 bytes)
  readme.md (100 bytes)
  .gitignore (50 bytes)
  src/ (1200 bytes)
    main.py (500 bytes)
    utils.py (300 bytes)
    tests/ (400 bytes)
      test_main.py (400 bytes)

Total size: 1350 bytes
  • Iterator — use iterator to traverse composite trees without exposing the tree structure to the client.
  • Visitor — visitor lets you add operations to composite trees without modifying node classes.
  • Decorator — decorator adds responsibilities to a single object; composite organises objects into tree structures.
  • Flyweight — use flyweight to share leaf node data when the composite tree contains many identical leaves.