Back to All Design Patterns
Pattern 17 of 22
Structural PatternADVANCED7 min deep dive
Flyweight Pattern Pattern
Fits more objects into the available amount of RAM by sharing common parts of state between multiple objects instead of keeping all of the data in each object.
Real-World Mental Model / Metaphor:
Rendering 1,000,000 trees in a video game forest. Rather than loading the 3D mesh and texture 1,000,000 times, you share 1 TreeType in RAM and only store unique (X, Y) coordinates for each tree.
Interactive Runtime Simulator
Test and tweak inputs dynamicallyInteractive Flyweight Pattern SimulatorLIVE PLAYGROUND
Interact with the controls below to see how this design pattern executes in memory at runtime.
Switch Target Factory Platform:
The client code only interacts with
GUIFactory.createButton() and GUIFactory.createCheckbox(). It never couples directly to concrete classes!CONCRETE PRODUCTS CREATEDmac
Rendered Button:Rendered Checkbox:
The Problem It Solves
Running out of RAM (Out of Memory) when instantiating millions of small objects that duplicate heavy immutable data.
The Architectural Solution
Separate Intrinsic State (shared, constant, read-only) from Extrinsic State (unique, contextual, passed via method arguments).
Pattern Structure & Participants
| Participant | Role & Responsibility | Type |
|---|---|---|
| TreeType (Flyweight) | Shared intrinsic state (name, color, 3D texture) | class |
| Tree (Context) | Extrinsic state (X, Y coordinates + reference to TreeType) | class |
| TreeFactory | Caches and returns existing TreeType instances | class |
Collaboration Rules:
- 1,000,000 Trees share only 3 TreeType instances in memory
- RAM usage drops from 2GB to 15MB
Production Code Implementation
Flyweight Pattern.java
// Intrinsic State (Shared in RAM)
public class TreeType {
private final String name;
private final String color;
private final byte[] texture3D; // 10MB texture
public TreeType(String name, String color, byte[] texture3D) {
this.name = name;
this.color = color;
this.texture3D = texture3D;
}
public void draw(int x, int y) {
System.out.println("Drawing " + name + " at coords (" + x + ", " + y + ")");
}
}
// Flyweight Factory (Cache)
public class TreeFactory {
private static final Map<String, TreeType> cache = new HashMap<>();
public static TreeType getTreeType(String name, String color) {
String key = name + "_" + color;
return cache.computeIfAbsent(key, k -> new TreeType(name, color, new byte[1024]));
}
}
// Extrinsic State (Context)
public class Tree {
private final int x;
private final int y;
private final TreeType type; // Shared reference
public Tree(int x, int y, TreeType type) {
this.x = x;
this.y = y;
this.type = type;
}
public void draw() { type.draw(x, y); }
} When to Use:
- •Only when your program must spawn a huge number of similar objects that exhaust available RAM.
When to Avoid (Anti-Pattern):
- •When memory is not a bottleneck (Flyweight introduces code complexity and minor CPU overhead).
Staff Engineering Interview Insights & Gotchas
- Mention `Integer.valueOf(100) == Integer.valueOf(100)` returning `true` because of the JVM Integer cache flyweight (-128 to 127).