skip to content

questions

6

What is the Flyweight design pattern, and what problem is it meant to solve?

level: juniorimportance: must knowfreq 55%

answer

  1. Many fine-grained objects, few distinct values
  2. Intrinsic = shared and immutable
  3. Extrinsic = passed in by the caller
  4. Factory interns: one instance per key
  5. Wins only when N ≫ K

basics

~20 s

Flyweight saves memory when a program needs a huge number of similar objects. Instead of one object per occurrence, identical parts are stored once and shared; the parts that differ per occurrence are passed in by the caller.

solid answer

~50 s

Flyweight is a structural pattern whose intent is to support very large numbers of fine-grained objects efficiently by sharing them. Each object's state is split in two: intrinsic state — the values that are identical across many occurrences and independent of where the object is used (a character's glyph shape, a tree species' mesh and texture, a chess piece's movement rules) — and extrinsic state, which depends on the usage context (position on the page, x/y coordinates, board square). Intrinsic state is stored in a shared, immutable flyweight object created and handed out by a factory that guarantees one instance per distinct value. Extrinsic state is kept by the client and passed as arguments to flyweight operations, so the shared object never holds it. The payoff is memory: N occurrences cost N small context records plus K distinct flyweights instead of N full objects. The cost is a less convenient API, an extra lookup, and a hard requirement that shared state be immutable.

go deeper

for a junior

Say it shares identical objects to save memory, and give one concrete example (characters in a document, trees in a scene).

for a middle

Name intrinsic vs extrinsic state explicitly, describe the factory that returns cached instances, and state the N ≫ K precondition.

for a senior

Add the cost model, the immutability and identity constraints, the factory-map lifetime/leak risk, and real-world instances such as string interning or dictionary-encoded columns.

for a principal

Frame Flyweight as one point on a spectrum of deduplication/representation choices (interning, dictionary encoding, structure-of-arrays, compression) and discuss when to push the concern below the API instead of into it.

## The problem Some designs are conceptually cleanest when every little thing is an object: every character in a document, every tree in a forest scene, every cell in a spreadsheet, every particle in a simulation. But if a document has 2,000,000 characters and each character object carries a font family, size, weight, colour, kerning table and glyph outline, you may need hundreds of bytes per character — hundreds of megabytes for a document whose text is 2 MB. The object model is right; the *representation* is wasteful, because those 2,000,000 objects only take on a few hundred genuinely distinct combinations of font attributes. **Flyweight** (from the *Design Patterns* / "Gang of Four" catalogue, structural category) is the pattern that keeps the fine-grained object model while removing the duplication. ## Core idea: split the state Flyweight partitions an object's fields into two groups. - **Intrinsic state** — values that are *context-free*: they are the same no matter where or how the object is used, and they are what makes two occurrences "the same thing". Examples: the glyph outline for the letter `a` in Times 12pt bold; the mesh and bark texture of an oak; the movement rules of a knight; the string `"US"` used as a country code. Intrinsic state lives inside the shared flyweight and must be **immutable**, because many unrelated clients see the same instance. - **Extrinsic state** — values that are *context-dependent*: they differ per occurrence and cannot be shared. Examples: the (x, y) position of that letter on the page; the coordinates, scale and rotation of that oak; the square the knight stands on; which row of a table holds `"US"`. Extrinsic state is stored by the client (often compactly, in parallel arrays or small structs) and **passed as parameters** into flyweight methods: `glyph.draw(canvas, x, y)` rather than `glyph.draw(canvas)`. ## Core mechanism: the factory Clients must not construct flyweights directly, or sharing breaks. A **flyweight factory** owns a map from *intrinsic key* → flyweight instance and returns an existing instance when the key repeats, creating one only on first request. This "one canonical instance per distinct value" behaviour is also called **interning**. The map is the only thing that grows with the number of *distinct* values, not with the number of *uses*. ## The arithmetic Let `N` be the number of occurrences, `K` the number of distinct intrinsic values, `S_i` the size of intrinsic state and `S_e` the size of extrinsic state. Without Flyweight you pay roughly `N × (S_i + S_e + object header)`. With Flyweight you pay `K × S_i + N × S_e + N × (a reference) + factory-map overhead`. Flyweight wins when `N ≫ K` and `S_i` is not tiny relative to `S_e`. If every occurrence is distinct (`K ≈ N`) you have added a hash map and gained nothing. ## Consequences - **Pro:** dramatic memory reduction; better cache locality (fewer distinct objects touched); shared immutable objects are trivially thread-safe to read; equality checks can often become reference checks. - **Con:** the API becomes parameter-heavy because extrinsic state must be threaded through call sites; there is a lookup cost on acquisition; the factory table itself consumes memory and, if unbounded and strongly referenced, can leak; and sharing forbids mutation, so any "customise this one instance" requirement forces you back out of the pattern. ## Where you have already seen it String interning, small-integer caches in many runtimes' boxing (e.g. `Integer.valueOf(-128..127)`), dictionary/​symbol encoding of repeated column values in analytical databases and Parquet/ORC files, enum constants, immutable colour/font registries in GUI toolkits, tile and sprite atlases in games, and the entity-component layouts where shared archetype data is separated from per-entity transforms. The pattern is far more common in library internals than in application code, which is exactly what you should say if asked whether you have "used" it.

  • Is Flyweight about speed or about memory?
    Primarily memory. Speed effects are indirect: fewer allocations and better cache locality can help, but the lookup in the factory and the extra parameter passing can also cost time. If your goal is latency, measure — do not assume Flyweight is faster.
  • Does Flyweight require immutability?
    The intrinsic state must be immutable in practice, because one instance is visible to many unrelated clients; mutating it would silently change every occurrence. Extrinsic state is owned by the client and may be mutable.

A print shop has one physical metal stamp per letter. To print a page it does not cast 2,000 stamps; it reuses ~40 stamps and records where on the page each is pressed. The stamp is intrinsic and shared; the press position is extrinsic and per-use.

saying these in an interview costs you the question

  • Describing Flyweight as a speed optimization first, ignoring that its stated intent is memory
  • Claiming Flyweight means 'only one instance exists' — that is Singleton; Flyweight is one instance per distinct value
  • Storing per-occurrence data such as coordinates inside the shared object
  • Letting clients call the flyweight constructor directly, which destroys sharing
  • Applying it when nearly every object is unique, so the cache costs more than it saves

context

open as a page

How do you decide which fields of an object are intrinsic (shareable) versus extrinsic (per-context) when applying the Flyweight pattern, and what happens if you get the split wrong?

level: middleimportance: must knowfreq 45%

basics

~20 s

Ask: "would this value be the same for every place the object is used?" If yes it is intrinsic and can be shared; if it changes per use (position, owner, timestamp) it is extrinsic and must be passed in. Putting per-use data in the shared object corrupts all users.

open as a page

How does the Flyweight pattern differ from an object pool, a cache, memoization, and Singleton — patterns that also hand out reused instances?

level: middleimportance: should knowfreq 40%

basics

~20 s

All reuse objects, but for different reasons. Flyweight shares immutable value-like objects concurrently to save memory. A pool lends out mutable objects one user at a time and takes them back. A cache stores results to avoid recomputation and may evict them. Singleton just limits a class to one instance.

open as a page

How should the flyweight factory be implemented — the cache/interning strategy behind the Flyweight pattern — and what concurrency and lifetime issues does it introduce?

level: seniorimportance: should knowfreq 35%

basics

~20 s

The factory keeps a map from the shared-state key to the single instance, creating one only on a miss. It must be thread-safe, must not let two callers get two different instances for the same key, and must have a bounding or eviction policy so the map does not grow forever.

open as a page

How do you decide whether applying the Flyweight pattern will actually pay off, and when should you reject it?

level: seniorimportance: should knowfreq 30%

basics

~20 s

It pays off when you have very many objects but few distinct values, the duplicated part is big enough to matter, and the objects are long-lived. Reject it when values are mostly unique, the objects are few or short-lived, or the API damage outweighs the memory saved.

open as a page

What hazards arise from the shared, interned instances that the Flyweight pattern creates — around identity comparison, mutability, locking, serialization and multi-tenancy?

level: principalimportance: nice to knowfreq 18%

basics

~20 s

Because one object is shared everywhere, any mutation of it affects every user; comparing by reference works until an instance is created outside the factory or evicted; locking on a shared instance can make unrelated code contend or deadlock; and copies from serialization or other tenants silently break the "one instance" assumption.

open as a page