Composition over Inheritance: The Modular Entity Assembly
The Gorilla-Banana Problem
In Lesson 04, we used Inheritance to share kinematic physics. However, as an engineering system grows, strict ontological "is-a" relationships become a massive liability.
The creator of Erlang, Joe Armstrong, famously summarized this architectural trap: "You wanted a banana but what you got was a gorilla holding the banana and the entire jungle." When you inherit from a large Base Class, you inherit everything—including internal state and methods your subclass might never actually use.
The Taxonomy Trap: The Diamond Problem
Imagine building a multi-physics simulator. You have a RigidBody class (handling collision math) and a ConductiveBody class (handling electromagnetic fields).
What happens when you need to model a Copper Sphere? It is both rigid and conductive. JavaScript, like many languages, does not support multiple inheritance. You are forced to either duplicate the electromagnetic math into the RigidBody chain, or build an absurdly deep, bloated hierarchy (e.g., ConductiveRigidBody).
The Solution: Entity-Component Assembly
Composition solves this by relying on a "has-a" relationship rather than "is-a". Instead of forcing a Copper Sphere to inherit its physics, we build an empty structural container (an Entity) and plug independent mathematical modules (Components) into it.
This perfectly aligns with our core philosophy:
"Complexity is the enemy of scale. Abstraction is the art of hiding the complex inner workings of a system and providing a simple, intuitive interface for the user."
By composing modules, the entity itself becomes a simple, high-level abstraction over isolated, highly complex physical behaviors.
{/* prettier-ignore */}
Architectural Flexibility
Using composition allows you to alter a system's physical properties at runtime, something inheritance strictly prohibits. If the sphere reaches its melting point during the simulation, you simply detach the RigidBody module and attach a FluidDynamics module. The core entity survives, but its behavior adapts.
| Vector of Comparison | Inheritance (is-a) | Composition (has-a) |
|---|---|---|
| Coupling | Tightly coupled (Child relies on Parent's internal structure) | Loosely coupled (Modules do not interact with each other directly) |
| Extensibility | Rigid (Changes require refactoring the entire tree) | Highly modular (Plug-and-play assembly) |
| Memory Overhead | Bloated (Inherits unused methods) | Lean (Contains exactly what is attached) |
What You Have Learned
- The Gorilla-Banana Problem: Inheritance forces you to pull in unneeded architectural weight.
- Modular Composition: Building objects by mixing together independent functional modules (like LEGO bricks) rather than relying on a rigid family tree.
- Flat Architecture: Composition keeps your codebase flat, preventing the deep nesting that makes enterprise-level simulations impossible to debug.
- Runtime Flexibility: Systems composed of modules can dynamically swap behaviors while the engine is running.
In the next lesson: Interfaces & Contracts — If we are assembling objects dynamically from independent modules, how do we mathematically guarantee they have the correct methods? We enforce strict structural contracts.