A text editor with 50,000 characters on screen. If each character is an object holding its rendered glyph bitmap, that is 50,000 copies of the letter shapes — and there are only about 96 distinct letters in use.
The split
Divide every object’s data in two:
- Intrinsic — the same for everything that shares this identity. The glyph
bitmap for
e. Does not depend on where theeis. - Extrinsic — different per occurrence. Position, colour, selection state.
Store the intrinsic part once and pass the extrinsic part in at call time:
class Glyph { // intrinsic: one per distinct character constructor(readonly char: string, readonly bitmap: Bitmap) {}
draw(x: number, y: number, colour: Colour) { // extrinsic: passed in canvas.blit(this.bitmap, x, y, colour); }}
const GLYPHS = new Map<string, Glyph>();function glyphFor(char: string): Glyph { return GLYPHS.get(char) ?? GLYPHS.set(char, new Glyph(char, render(char))).get(char)!;}Move the sliders — note which one actually changes the total:
- one object each
- 205.6 MB
- shared glyphs
- 1.2 MB
- ratio
- 172×
A glyph bitmap is ~4 kB and does not depend on where it appears; a position is 16 bytes and does. Storing the bitmap once per distinct character rather than once per occurrence is the entire pattern — and note that raising the distinct count barely moves the total, while raising the character count moves it a lot.
Flyweights must be immutable
If two thousand es share one Glyph, then mutating it changes all of them.
glyphFor('e').colour = 'red'; // every e on the page is now redSo intrinsic state has to be readonly, set at construction, and never
reachable for modification. In practice this means the factory is the only thing
that constructs them, and the class exposes no setters.
Where it already is
String interning. Java’s String.intern() and JavaScript’s Symbol.for()
guarantee that equal strings share one object. Most language runtimes intern
short strings and small integers automatically — Integer.valueOf(5) in Java
returns a cached instance for anything from −128 to 127, which is why == works
for small integers and stops working at 128.
Game engines. One mesh and one texture, thousands of trees. The per-tree data is a transform matrix.
Immutable value objects. A Money or a Colour that never changes can be
shared safely by definition. Most flyweight usage in modern code is this, and
nobody calls it a pattern — they just say “value type”.