Skip to content

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 : uses

When 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.

idle
Click Run TS to execute. First run downloads Babel (~400 KB, cached after that).

Tradeoffs

ProCon
Large memory savings when many objects share stateExtrinsic state must be passed to every method call
Reduces object creation costCode is harder to read: state lives in two places
Works transparently once the factory is in placeThread-safety requires locking the factory cache
Classic use: game engines, text renderersOnly 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 a weakref.WeakValueDictionary for the cache if you want automatic eviction when no external references remain.
  • In Go, protect the factory map with a sync.Mutex or use sync.Map for 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, 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