Flyweight Pattern
Share repeated immutable state while keeping per-use data such as position separate.
· Last updated: 2026-10-25 · 6 min read
Overview
A document may contain ten thousand glyphs but only a few font-and-size combinations. A separate style object for every glyph duplicates the same state.
Flyweight shares the state that can safely be reused. Shared internal data is called intrinsic state; data that varies by use is extrinsic state. Here style is shared while placement stays separate.
UML
StylePool reuses a GlyphStyle for each font-and-size pair. PlacedGlyph refers to that style but keeps its own letter and X coordinate.
History
GoF cataloged Flyweight as a structural pattern in Design Patterns in 1994. It uses shared state to support many small objects with less duplicated storage.1
Why use it?
Separating shared state from per-use state allows reuse. Mutating shared state would affect several users, so GlyphStyle is immutable.
A pool also grows if new keys accumulate. Define its lifetime and retention policy, and measure the workload before claiming a memory benefit.
Example
Request the same style twice and compare object identity.
using System;
using System.Collections.Generic;
using System.Linq;
var pool = new StylePool();
Console.WriteLine(ReferenceEquals(pool.Get("Serif", 16), pool.Get("Serif", 16)));
// Put only immutable, shareable values in the style object.
sealed record GlyphStyle(string Font, int Size);
sealed record PlacedGlyph(char Letter, int X, GlyphStyle Style);
// Reuse the object for the same key instead of constructing a style per placement.
sealed class StylePool
{
private readonly Dictionary<(string Font, int Size), GlyphStyle> styles = new();
public int Count => styles.Count;
public GlyphStyle Get(string font, int size)
{
var key = (font, size);
if (!styles.TryGetValue(key, out var style))
{
style = new GlyphStyle(font, size);
styles.Add(key, style);
}
return style;
}
}
TrueStylePool returns the object it already created instead of constructing two equal style objects.
An extended example
Place two different letters at different coordinates while sharing one style.
using System;
using System.Collections.Generic;
using System.Linq;
var pool = new StylePool();
var letters = new[]
{
new PlacedGlyph('A', 0, pool.Get("Serif", 16)),
new PlacedGlyph('B', 20, pool.Get("Serif", 16))
};
foreach (var letter in letters) Console.WriteLine(letter.Letter + "@" + letter.X);
Console.WriteLine("styles: " + pool.Count);
// Put only immutable, shareable values in the style object.
sealed record GlyphStyle(string Font, int Size);
sealed record PlacedGlyph(char Letter, int X, GlyphStyle Style);
// Reuse the object for the same key instead of constructing a style per placement.
sealed class StylePool
{
private readonly Dictionary<(string Font, int Size), GlyphStyle> styles = new();
public int Count => styles.Count;
public GlyphStyle Get(string font, int size)
{
var key = (font, size);
if (!styles.TryGetValue(key, out var style))
{
style = new GlyphStyle(font, size);
styles.Add(key, style);
}
return style;
}
}
A@0
B@20
styles: 1The letters and positions differ; their style is shared. Singleton limits a class to one instance; this pool shares one object per font-and-size combination. This pool is used on one thread.
TypeScript example
Glyphs share font and size, but retain their own letter and position. Separating those values permits style reuse without mixing one placement’s data into another.
// Put only immutable, shareable values in the style object.
class GlyphStyle {
constructor(readonly font: string, readonly size: number) { Object.freeze(this); }
}
type PlacedGlyph = Readonly<{ letter: string; x: number; style: GlyphStyle }>;
// Reuse the object for the same key instead of constructing a style per placement.
class StylePool {
private readonly fonts = new Map<string, Map<number, GlyphStyle>>();
get(font: string, size: number): GlyphStyle {
let sizes = this.fonts.get(font);
if (!sizes) { sizes = new Map(); this.fonts.set(font, sizes); }
let style = sizes.get(size);
if (!style) { style = new GlyphStyle(font, size); sizes.set(size, style); }
return style;
}
get count(): number { return [...this.fonts.values()].reduce((n, sizes) => n + sizes.size, 0); }
}
const pool = new StylePool();
const letters: PlacedGlyph[] = [
{ letter: "A", x: 0, style: pool.get("Serif", 16) },
{ letter: "B", x: 20, style: pool.get("Serif", 16) },
];
for (const glyph of letters) console.log(glyph.letter + "@" + glyph.x);
console.log("styles: " + pool.count);A@0
B@20
styles: 1Object.freeze keeps the shared style immutable, and the pool looks up both font and size. Retention and size limits are separate policies omitted from this example.
Related reading
Each C# example is a separate .NET 10 console program.
Compile the TypeScript examples for ES2022 or later and run the emitted JavaScript with Node.js.
Footnotes
- Gamma, Helm, Johnson, Vlissides. Design Patterns (1994). Publisher record. Structural patterns, Flyweight. ↩