AcademyDesign Patterns
Flyweight pattern
Sharing the identical, expensive part of many objects instead of duplicating it, the strict immutability it demands, and one write to shared state corrupting every instance.
6 min readUpdated 3 exercises
Flyweight is for when there are simply too many objects, most of whose data is identical. It splits what's shared and expensive from what's unique and has to stay per-instance.
A Flyweight splits an object's data into the part that's identical across many instances, heavy and worth sharing, and the part that varies per instance, light and kept separate. Thousands of trees in a forest scene can share one mesh and texture per species, the expensive part, while each tree instance keeps only its own position, the cheap part that actually differs.
The problem it solves
You are rendering a forest with 100,000 trees. Each tree has a 3D mesh and a texture, a few megabytes together, and a position, a couple of numbers.
If every tree object carries its own copy of the mesh, memory use is 100,000 times the size of a mesh, even though there are only ten species. The program slows down, the garbage collector struggles, and eventually the tab crashes.
- The waste is in duplication, not in the data itself. There are only ten different meshes.
- The unique part is tiny. Position and rotation are a handful of bytes.
- Creating the shared part is slow. Loading a mesh 100,000 times costs time as well as memory.
The fix is to load each mesh once, share it between every tree of that species, and keep only the position on each tree.
Structure
Two kinds of state:
- Intrinsic state: identical across instances and immutable. It lives in the flyweight (
TreeType: name and mesh). - Extrinsic state: different per instance. It lives in the context object (
Tree:xandy) or is passed in at call time.
Three parts:
- The flyweight (
TreeType), holding the shared data. - A factory (
TreeTypeFactory), which returns an existing flyweight when one matches and creates it only when none does. - The context (
Tree), which keeps its own position and a reference to the flyweight.
class TreeType {
constructor(name, mesh) {
this.name = name;
this.mesh = mesh;
}
render(x, y) { return this.name + '@' + x + ',' + y + ' using ' + this.mesh; }
}
class TreeTypeFactory {
static types = new Map();
static get(name, mesh) {
if (!TreeTypeFactory.types.has(name)) {
TreeTypeFactory.types.set(name, new TreeType(name, mesh));
}
return TreeTypeFactory.types.get(name);
}
}
class Tree {
constructor(x, y, type) {
this.x = x;
this.y = y;
this.type = type;
}
render() { return this.type.render(this.x, this.y); }
}
// const oak = TreeTypeFactory.get('oak', 'oak-mesh.glb');
// new Tree(10, 20, oak).render();
// new Tree(30, 40, oak).render(); — same TreeType instance, reusedThe factory is what makes sharing real. Without it, callers would create their own TreeType and nothing would be shared. The last two lines of the example produce two trees that point at the very same TreeType object.
Where it is used in real systems
| Use case | Shared (intrinsic) | Per instance (extrinsic) |
|---|---|---|
| Games and 3D scenes | Mesh and texture per species | Position, rotation |
| Text editors | Glyph shape for each character | Position, font size, color |
| Maps | Marker icon per category | Coordinates |
| Particle systems | Sprite image | Position, velocity, age |
| Table and cell styles | Style object per combination | The cell it applies to |
| String handling | One copy of each distinct string | Where it is used |
| Icon libraries | One SVG definition | Each place the icon appears |
The signal is the same each time: a huge number of similar objects and a small number of distinct values behind them.
Flyweights you already use
- String interning. Many runtimes keep one copy of identical strings. JavaScript engines do this for property names and literals.
- The browser's image and font caches. One decoded image or font file, used by every element that references it.
- SVG `<use>` and sprite sheets. One definition drawn many times.
- React `memo` and constant objects. Hoisting a constant style object out of a component so every render shares it is a small flyweight.
- Connection pools. Not a flyweight strictly, but the same principle of sharing a costly object among many users.
The rules that make it safe
Sharing is only safe if the shared part cannot change.
- Flyweights must be immutable. If one tree changes
type.mesh, every tree of that species changes. Freeze the shared state, or expose no setters. - Extrinsic state must not leak in. Do not store
xandyon the flyweight. That would make each instance unique again, and undo the sharing. - Use a factory, not `new`. The whole point is that callers cannot create duplicates.
When not to use it
- There are few objects. With 50 trees the memory is negligible, and the factory is pure overhead.
- Little is actually shared. If every instance has mostly unique data, there is nothing to factor out.
- You have not measured. Flyweight trades simplicity for memory. Profile first, and add it when the numbers show a problem.
- The shared part must change per instance. Then it is not intrinsic state.
Also note that the code becomes harder to read: state is split across two objects, and callers must remember to pass extrinsic state where needed.
What interviewers ask
- "What is the Flyweight pattern?" Sharing the common part of many similar objects to save memory.
- "Intrinsic versus extrinsic state?" Intrinsic is shared and immutable. Extrinsic varies per instance and is stored outside the flyweight.
- "Why is a factory needed?" To guarantee that identical flyweights are reused rather than created again.
- "Give a real example." Characters in a text editor, trees in a game, or string interning.
- "What is the downside?" More complex code and the discipline of keeping shared state immutable.
The mistake to avoid
A Flyweight that stores per-instance data. The moment TreeType holds an x or a mutable counter, two trees can no longer share it, or worse, they share it and corrupt each other. Keep the flyweight strictly to data that is identical for every user of it, and freeze it.
The mechanism above, a factory that hands back an existing object instead of creating a new one, is the same in JavaScript, Python or Java. Only the syntax changes.
Now practice it
1 challenge · 2 quizzesReading this page is the cheap half. These are the exercises that make you use it.
- FlyweightProCache the expensive, shared part of many objects behind a factory and prove instances reuse it — the sharing this page's diagram is built around.ChallengeSolve · MediumChallengeSolve · Medium
- Design Patterns · Structural PatternsTen quick questions across Adapter, Decorator, Facade, Proxy, Bridge, Composite and Flyweight, to check the mental model before this one.QuizTake · MediumQuizTake · Medium
- Design Patterns · Structural Patterns — Series 2ProA second, tougher pass on the same structural family, for once the first set feels easy.QuizTake · MediumQuizTake · Medium
Read next
Composite pattern
Treating a single object and a group of them through one interface, the cost every leaf pays for methods it may never use, and a leaf that accepts children it cannot hold.
6 min
Bridge pattern
Letting two dimensions of variation grow independently instead of multiplying into N times M classes, the two hierarchies it costs, and the if/else sneaking that growth back.
6 min