Flyweight Pattern

Flyweight Pattern

Use sharing to support a large number of fine-grained objects efficiently by separating intrinsic (shared) state from extrinsic (context-specific) state.

Intent

The Flyweight pattern divides object state into two categories. Intrinsic state is stored inside the flyweight object: it is shared across all contexts where the flyweight is used, and it is immutable — it never changes after construction. Extrinsic state is not stored in the flyweight: it varies per context and is passed by the caller at call time. A FlyweightFactory maintains a cache (typically a Map keyed on intrinsic state) and returns the existing shared instance rather than creating a new one. The memory reduction comes from having one flyweight object serve thousands of different calling contexts — each context passes its own extrinsic state, so the flyweight remains reusable.

Real-world examples: Java String pool (interning identical strings to one object), browser text rendering glyph caches (one GlyphFlyweight per character+font pair serves every occurrence on the page), game engine particle systems (one Particle flyweight per asset type serves millions of on-screen particles with per-particle position/velocity as extrinsic state).

When NOT to Use

  • Object count is modest (hundreds) — the factory cache and intrinsic/extrinsic split add complexity that is premature optimisation at this scale
  • No significant shared state exists — if there is nothing to extract as intrinsic state, the pattern does not apply
  • Extrinsic state is difficult to compute or pass at call time — the coordination cost outweighs the memory savings
  • The objects are not performance-sensitive — simpler designs are preferred when memory pressure is not a concern

When to Use

  • The application creates a very large number of objects (thousands to millions) and memory pressure is measurable
  • Objects share significant common state that can be extracted as intrinsic (immutable, shared)
  • Extrinsic state can be computed or passed by callers at call time, rather than stored per-object
  • Memory reduction justifies the added complexity (benchmark before applying — measure the savings first)

How It Works

Participants:

  • Flyweight — the interface declaring the operation that accepts extrinsic state as parameters.
  • ConcreteFlyweight — stores intrinsic state (immutable, set at construction); implements the Flyweight operation, using the passed extrinsic parameters for context-specific behaviour.
  • FlyweightFactory — maintains a cache Map<key, Flyweight>; on a get(key) call, returns the existing shared instance if present, or creates and caches a new one. Often implemented as a Singleton.
  • Client — holds extrinsic state per context; retrieves flyweights from the factory; passes extrinsic state as arguments to each operation call.

The key design decision at implementation time is identifying what is intrinsic versus extrinsic. A useful heuristic: intrinsic state is whatever would be identical across all occurrences of a flyweight in different contexts.

Class Diagram

classDiagram
    class Flyweight {
        <<interface>>
        +operation(extrinsicState)*
    }
    class ConcreteFlyweight {
        -intrinsicState
        +operation(extrinsicState)
    }
    class FlyweightFactory {
        -cache : Map~key, Flyweight~
        +getFlyweight(key) Flyweight
    }
    class Context {
        -extrinsicState
        -flyweight : Flyweight
        +render()
    }
    Flyweight <|.. ConcreteFlyweight
    FlyweightFactory o-- Flyweight : manages pool
    Context --> Flyweight : uses
    FlyweightFactory ..> ConcreteFlyweight : creates if absent

    note for ConcreteFlyweight "Intrinsic state: shared, immutable\n(e.g., glyph shape, icon texture)"
    note for Context "Extrinsic state: unique per use,\npassed at runtime (e.g., position, colour)"

TypeScript Example

// Flyweight — TypeScript
// Source: GoF Design Patterns, p.195
// Intrinsic state: shared, immutable (character, font)
// Extrinsic state: passed by caller (position, color)
 
class GlyphFlyweight {
  constructor(
    private readonly char: string,   // intrinsic
    private readonly font: string    // intrinsic
  ) {}
 
  render(x: number, y: number, color: string) {  // extrinsic
    console.log(`Render '${this.char}' at (${x},${y}) ${color} [${this.font}]`);
  }
}
 
class GlyphFactory {
  private cache = new Map<string, GlyphFlyweight>();
  get(char: string, font: string): GlyphFlyweight {
    const key = `${char}:${font}`;
    if (!this.cache.has(key)) this.cache.set(key, new GlyphFlyweight(char, font));
    return this.cache.get(key)!;
  }
}
 
// Usage — one GlyphFlyweight per char+font, reused across thousands of render calls
const factory = new GlyphFactory();
const g = factory.get("A", "Arial");
g.render(10, 20, "black");   // extrinsic: position and color vary per call
g.render(50, 80, "red");     // same flyweight instance, different extrinsic state

Java Example

// Flyweight — Java
// Source: GoF Design Patterns, p.195
class GlyphFlyweight {
    private final String ch;   // intrinsic
    private final String font; // intrinsic
    GlyphFlyweight(String ch, String font) { this.ch = ch; this.font = font; }
    void render(int x, int y, String color) {  // extrinsic
        System.out.printf("Render '%s' at (%d,%d) %s [%s]%n", ch, x, y, color, font);
    }
}
 
class GlyphFactory {
    private final Map<String, GlyphFlyweight> cache = new HashMap<>();
    GlyphFlyweight get(String ch, String font) {
        String key = ch + ":" + font;
        return cache.computeIfAbsent(key, k -> new GlyphFlyweight(ch, font));
    }
}
PatternRelationship
Composite-PatternFlyweight is often used inside Composite leaf nodes to share state across many similar leaves
Proxy-PatternBoth involve a level of indirection; Proxy controls access to an object, Flyweight shares state across contexts
Singleton-PatternFlyweightFactory is often implemented as a Singleton to ensure one shared cache exists per application

Sources

  • Gamma, Helm, Johnson, Vlissides — Design Patterns, Addison-Wesley, 1994, p.195

Notes that link here: GoF-Patterns-MOC