The Flyweight Pattern Solution
The new code uses the Flyweight Pattern, a structural design pattern that saves memory by sharing data between similar objects. It splits an object's data into Intrinsic (shared) and Extrinsic (unique) parts.
1. How Flyweight Pattern Solves the Issue
TreeType Class (The Flyweight)
This class is the flyweight object. It stores the data that all trees of one type have in common: name, color, and texture. We do not copy this data into every tree. We create only one instance for each different combination.
Tree Class (The Context)
This class is now light. It no longer holds the strings. It stores only:
- Extrinsic data: $x, y$ coordinates (unique to each specific tree's location).
- Reference to shared data: One reference to a
TreeTypeinstance.
TreeFactory Class (The Cache)
This is the central factory that makes sure TreeType instances are reused. Before it creates a new type, it checks its map (treeTypeMap) for the same name, color, and texture. If the type is there, it returns it. If not, it creates it once and stores it.
2. Key Improvements
Dramatic Memory Efficiency
Even with 1 million trees, if they all share the same properties ("Oak", "Green", "Rough"), only one TreeType object is created. So the repeated strings, which took most of the memory, shrink to one shared object. Each Tree still costs some memory (see the table below).
Reduced Object Overhead
The heavy data now lives in a shared object, so each Tree is small. The demo still creates 1 million Tree objects, but each holds only two ints and a reference. The same memory holds far more trees than before, when the program could fail with an OutOfMemoryError.
Optimized Performance
- Garbage Collection: There are fewer heavy objects to track, so the Java Virtual Machine (JVM) spends less time on garbage collection and the program runs more smoothly.
- Rendering: Modern graphics engines can use "instanced rendering" (drawing many copies of one model in one go) when objects share the same mesh or texture data. Flyweight makes that sharing possible.
3. Structural Breakdown
| Component | Responsibility |
|---|---|
Flyweight (TreeType) | Contains the Intrinsic State (shared data). It should be immutable (final fields, never changed after creation), so one user cannot affect the others. The demo's fields are not final, but it has no setters. |
Context (Tree) | Contains the Extrinsic State (unique data) and a reference to the Flyweight. |
Flyweight Factory (TreeFactory) | Manages the creation and caching of Flyweight objects. |
Client (Forest) | Works out or stores the extrinsic state and passes it to the Flyweight methods. |
4. Comparison of "Before" vs "After"
These numbers are illustrative, not measured. Before: the repo code assumes 1KB per tree, so about 1GB. After: each Tree is about 32 bytes (header, two ints, one reference), so about 30MB.
| Feature | Before (Heavyweight) | After (Flyweight) |
|---|---|---|
| Memory for 1M Trees | Very High (~1GB) | Low (~30MB) |
| Redundant Strings | 1,000,000 copies of "Oak" | 1 copy of "Oak" |
| Scalability | Limited by RAM | High; limited only by coordinates |
| State Management | Tangled; all data in one class | Separated into shared vs. unique |
5. Summary of "Sharing"
The Flyweight Pattern is like a Library. Students do not each buy a copy of a textbook, which costs money and space. The library keeps one copy that everyone shares. Each student only keeps their own bookmark (the extrinsic state like $x, y$).