flyweight - marcelstoltz00/Photosyntech GitHub Wiki
Minimizes memory consumption by sharing immutable data objects across multiple plant instances. It achieves this by externalizing a plant's extrinsic state and sharing its intrinsic state (e.g., season names, water strategies, sun strategies, maturity states) through a centralized caching mechanism.
flyweight/
├── Flyweight.h/.cpp # Wrapper for shared intrinsic state
└── FlyweightFactory.h/.cpp # Manages the cache of Flyweight objects
| Pattern Role | Photosyntech Class(es) | Responsibility |
|---|---|---|
| Flyweight | Flyweight<T> |
Wraps shared, immutable intrinsic state (T data) and provides access via getState(). Its destructor is responsible for deleting the wrapped raw pointer. |
| FlyweightFactory | FlyweightFactory<ID, T> |
Creates and manages a cache (std::unordered_map) of Flyweight<T> objects, keyed by ID. It returns existing instances if available or creates new ones. |
| Client |
LivingPlant, Inventory
|
LivingPlant holds pointers to Flyweight<T> instances for its intrinsic state. Inventory instantiates and manages the FlyweightFactory instances. |
| Intrinsic State |
std::string (for season names, plant names), WaterStrategy*, SunStrategy*, MaturityState*
|
The shared, immutable data objects that are managed and reused by the Flyweight pattern. |
- FR-10: Shared Data Memory Optimization: Directly addresses this by minimizing memory usage through aggressive sharing of immutable data, significantly reducing the memory footprint for large numbers of plants.
- NFR-4: Scalability: Essential for enabling the system to efficiently handle simulations with up to 100 million unique plant instances without memory exhaustion, ensuring memory scales sub-linearly with plant count.
- NFR-1: Performance: Reduces memory allocation overhead and improves cache locality, contributing to better overall system performance.
-
FR-6: Centralized Inventory: Works in conjunction with the
Inventorysingleton to centralize the management and access of all shared resources.
The Flyweight pattern is fundamental for memory optimization and scalability within the Photosyntech system:
-
Managed by Singleton: Three primary
FlyweightFactoryinstances (for strings, strategies, and states) are instantiated and managed by theInventorysingleton, providing global, centralized access to shared objects. -
Efficient Retrieval: When a client (e.g., a
LivingPlantinstance) needs an intrinsic state object (like a watering strategy), it requests aFlyweight<T>from the appropriateFlyweightFactoryvia theInventorysingleton. The factory quickly returns a cached instance if available or creates a new one. -
Immutable Sharing: Flyweight objects are designed to be immutable once created, allowing them to be safely shared across thousands or millions of
LivingPlantinstances. EachLivingPlantholds only a pointer to itsFlyweightinstances, rather than a unique copy of the data. -
Ownership and Lifetime: The
FlyweightFactoryowns theFlyweight<T>objects it creates and manages their lifetime. Crucially, theFlyweight<T>object itself owns the raw pointerT datait wraps anddeletes it in its destructor. -
Pattern Integration:
-
Singleton Pattern: The
Inventorysingleton owns and provides access to allFlyweightFactoryinstances. -
Builder Pattern: Builders retrieve strategy and state flyweights from the
Inventoryduring plant construction. -
Strategy Pattern:
WaterStrategyandSunStrategyobjects are explicitly managed as flyweights. -
Prototype Pattern: Cloned plants (via
LivingPlant's copy constructor) perform shallow copies of theirFlyweight<T>pointers, further reinforcing memory efficiency across cloned instances.
-
Singleton Pattern: The
The Flyweight pattern was chosen for its critical role in addressing performance and scalability challenges:
- Massive Duplication: The system potentially handles millions of plants, each requiring references to the same limited set of strategies, states, and season names. Without Flyweight, this would lead to colossal memory consumption.
- Immutability of Intrinsic State: Data like strategies, states, and string names are immutable once defined, making them perfect candidates for sharing.
- Memory Optimization Requirements: The NFR-4 requirement for supporting 100 million plants necessitates aggressive memory optimization, which Flyweight provides by ensuring a sub-linear memory footprint.
-
Performance: Reusing existing objects from the cache (
std::unordered_map) is significantly faster than repeated allocation and deallocation for new objects. - Simplified Client Code: Clients can use the objects as normal, unaware that they are shared instances, simplifying the application logic.
