Flyweight Pattern
The problem
Some applications create enormous numbers of nearly identical objects. A game rendering a forest of 100,000 trees can’t afford 100,000 independent objects each carrying the full tree species name, color, and texture data. A text editor displaying a million characters can’t store the full font descriptor on every glyph. The memory cost of repeated identical state adds up to something the application can’t sustain.
The Flyweight pattern separates object state into two buckets. Intrinsic state is shared and context-independent: tree species, color, texture. Extrinsic state is per-instance and context-dependent: the x/y coordinates where this particular tree is planted. The flyweight object stores only intrinsic state and is cached so that all instances sharing the same intrinsic values reuse one object. Extrinsic state is passed in at call time, not stored on the flyweight.
Structure
classDiagram class TreeType { +name: string +color: string +texture: string +draw(x, y) } class TreeTypeFactory { -types: Map +getTreeType(name, color, texture) TreeType +count() number } class Tree { -x: number -y: number -type: TreeType +draw() } class Forest { -trees: Tree[] +plantTree(x, y, name, color, texture) +draw() } Tree --> TreeType : uses (shared) TreeTypeFactory --> TreeType : creates and caches Forest --> Tree : contains Forest --> TreeTypeFactory : usesWhen to use
- You have a large number of objects (hundreds to millions) and memory is a measurable constraint.
- Most of the per-object state is identical across many instances and can be extracted as intrinsic state.
- The application does not require each object to have its own unique identity for the shared portion of its state.
- The extrinsic state can be computed or passed in at call time without burdening callers significantly.
Implementation
A Forest plants trees using a shared TreeType for species data (intrinsic state: name, color, texture) and per-tree coordinates (extrinsic state). Four trees share only two TreeType instances, demonstrating the memory savings. Python uses a module-level _tree_types dict as the factory cache, with a tuple key that is hashable and unambiguous. Go uses a treeTypeFactory with a sync.Mutex-guarded map to keep the cache safe under concurrent access.
Click Run TS to execute. First run downloads Babel (~400 KB, cached after that).
Click Run Python to execute. First run downloads Python (~10 MB, cached after that).
Click Run Go to execute. Runs via the Go Playground API.
Tradeoffs
| Pro | Con |
|---|---|
| Large memory savings when many objects share state | Extrinsic state must be passed to every method call |
| Reduces object creation cost | Code is harder to read: state lives in two places |
| Works transparently once the factory is in place | Thread-safety requires locking the factory cache |
| Classic use: game engines, text renderers | Only worthwhile at scale (hundreds to millions of objects) |
Gotchas
- If the intrinsic state is truly unique per object, Flyweight achieves nothing. Profile first before restructuring around it.
- The flyweight object must be immutable. If callers can modify shared state, all users of that flyweight are affected at once.
- In Python,
__slots__on the flyweight class eliminates the per-instance__dict__, squeezing out additional memory. Pair with aweakref.WeakValueDictionaryfor the cache if you want automatic eviction when no external references remain. - In Go, protect the factory map with a
sync.Mutexor usesync.Mapfor concurrent access. A plain map with no locking will race under goroutines. - Don’t apply Flyweight as a premature optimization. The intrinsic/extrinsic split makes the code meaningfully harder to follow. Only reach for it when a profiler confirms memory is the bottleneck.
References
- Design Patterns: Elements of Reusable Object-Oriented Software, the original GoF entry for Flyweight (p. 195)
- Flyweight pattern, Refactoring.Guru, illustrated walkthrough with a forest rendering example
- SourceMaking: Flyweight, discussion of intrinsic vs. extrinsic state
- Game Programming Patterns: Flyweight, Robert Nystrom’s take with game-engine context and clear diagrams
Related topics
- Design Patterns, the full GoF catalog
- Proxy, also a structural wrapper, but for access control rather than memory sharing
- Decorator, wraps objects to add behavior; Flyweight shares objects to save memory
- Facade, reduces visible complexity; Flyweight reduces memory footprint