intermediate

Composition over inheritance

Combine small collaborators when behavior varies independently instead of forcing a deep class hierarchy that couples unrelated changes.

Composition builds behavior by combining small collaborators—`class Report { constructor(private exporter: Exporter, private formatter: Formatter) {}`—instead of stacking overrides in a deep hierarchy. Behavior varies independently: swap `PdfExporter` without touching formatting logic.

Inheritance shares implementation but locks parent coupling; composition favors has-a relationships and explicit wiring (often via DI).

On interviews: refactor an overloaded base class into composed strategies and list what becomes easier to test.

Common pitfalls: god base classes, fragile template method overrides, and composing too many tiny objects without a clear facade.

The trade-off is more wiring boilerplate versus flexible, testable variation points.

Checklist:

  • Prefer has-a when behavior mixes independently.
  • Inject collaborators instead of extending.
  • Keep composed objects focused (SRP).