Creational Patterns: Resource Allocation and Instantiation
The Problem with Direct Allocation
Up to this point, we have used the new keyword to instantiate objects. In a small script, this is fine. In a massive engineering system, scattering new across hundreds of files is an architectural anti-pattern.
If every module in your physics engine explicitly calls new WebGLRenderer(), you have permanently hard-coded that specific renderer into your system. If you need to swap to a CanvasRenderer for a headless server simulation, you must refactor the entire codebase.
This brings us back to our governing law:
"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."
Creational patterns abstract the instantiation process. They hide the complexity of "how" and "which" objects are created, giving the system a clean steering wheel for requesting resources.
1. The Singleton: The Global Resource
Some resources represent singular physical or logical constraints. A hardware bus, a GPU context, or a master SimulationClock cannot be duplicated without causing catastrophic system conflicts.
The Singleton Pattern ensures that a class has exactly one memory allocation across the entire application, and provides a global access point to it.
{/* prettier-ignore */}
2. The Factory: The Dynamic Allocator
When reading a configuration file or a data stream, you often don't know exactly which class to instantiate until runtime.
The Factory Pattern acts as a centralized allocator. Instead of the high-level logic using new Particle() or new Photon(), it passes a specification to the Factory. The Factory reads the spec, handles the complex switch statements, and returns the correct instance.
{/* prettier-ignore */}
3. The Builder: The Sequential Constructor
In complex systems, an object might require 10 or 20 parameters to initialize properly. Passing 20 arguments into a single constructor new Environment(9.81, 1.002, true, false, ...) is an anti-pattern known as the Telescoping Constructor. It is unreadable and prone to errors.
The Builder Pattern abstracts object creation into a step-by-step sequential process. It allows you to construct a complex object piece by piece, chaining methods together to configure the state before finalizing the assembly.
{/* prettier-ignore */}
Engineering Trade-offs
| Pattern | Best Used For | Architectural Risk |
|---|---|---|
| Singleton | Hardware interfaces, global loop controllers. | Can become a glorified global variable, introducing hidden state dependencies. |
| Factory | Data-driven instantiation, plugin systems. | If the factory handles too many types, it violates the Single Responsibility Principle. |
| Builder | High-parameter configurations, multi-step math setups. | Requires writing an entire secondary class just to instantiate the first one. |
What You Have Learned
- Abstracting Instantiation: Creational patterns hide the
newkeyword, decoupling the "request for a resource" from the "allocation of the resource." - Singleton: Enforcing strict, singular memory limits for global constraints.
- Factory: Centralizing complex instantiation logic so the main system remains clean and open for extension.
- Builder: Replacing massive, unreadable constructor arguments with an elegant, chainable configuration pipeline.
In the final lesson: Behavioral Patterns — Once these objects are instantiated and loaded into memory, how do we architect their communication so the system does not collapse into a tangled mess of signals?