The Flyweight Design Pattern: Share to Save Memory
Master the Flyweight Pattern in Java. Learn how to support thousands of objects efficiently by sharing common state.
Moshiour Rahman
Advertisement
The Problem: Memory Explosion
Imagine a game with 1 million trees. Each tree has:
- Intrinsic state (shared):
treeType,texture,model(2MB each) - Extrinsic state (unique):
x,ycoordinates (8 bytes)
Naive approach:
1 million trees × 2MB = 2,000 GB of RAM 💥
But there are only 5 tree types. Why store the same texture 200,000 times?
The Solution: The Flyweight Pattern
The Flyweight Pattern minimizes memory by sharing as much data as possible with similar objects.
Real-Life Analogy: Chess Pieces ♟️
In a chess game:
- Shared (Intrinsic): Icon image, movement rules (same for all white pawns)
- Unique (Extrinsic): Current position on board
Instead of storing the icon 8 times for 8 pawns, share one icon and store only positions.
Visualizing the Pattern

Implementation
1. The Flyweight Class (Immutable Shared Object)
// Flyweight: Stores intrinsic (shared) state
public class TreeType {
private final String name;
private final String color;
private final String texture; // Large data (2MB)
public TreeType(String name, String color, String texture) {
this.name = name;
this.color = color;
this.texture = texture;
}
public void render(int x, int y) {
System.out.println("Rendering " + name + " at (" + x + ", " + y + ")");
// Use shared texture
}
}
2. The Flyweight Factory (Pool)
import java.util.HashMap;
import java.util.Map;
public class TreeFactory {
private static final Map<String, TreeType> treeTypes = new HashMap<>();
public static TreeType getTreeType(String name, String color, String texture) {
String key = name + color;
if (!treeTypes.containsKey(key)) {
treeTypes.put(key, new TreeType(name, color, texture));
System.out.println("Created new TreeType: " + key);
}
return treeTypes.get(key);
}
}
3. Context Object (Stores Extrinsic State)
// Context: Stores extrinsic (unique) state
public class Tree {
private int x, y; // Unique
private TreeType type; // Shared (flyweight)
public Tree(int x, int y, TreeType type) {
this.x = x;
this.y = y;
this.type = type;
}
public void render() {
type.render(x, y); // Pass extrinsic state
}
}
Usage
TreeFactory factory = new TreeFactory();
// Create 1 million trees with only 5 tree types
List<Tree> forest = new ArrayList<>();
for (int i = 0; i < 1_000_000; i++) {
int x = random.nextInt(10000);
int y = random.nextInt(10000);
String[] types = {"Oak", "Pine", "Birch", "Maple", "Spruce"};
String treeType = types[random.nextInt(5)];
TreeType sharedType = TreeFactory.getTreeType(treeType, "Green", "texture.png");
forest.add(new Tree(x, y, sharedType));
}
// Memory used:
// - 5 TreeType objects (5 × 2MB = 10MB)
// - 1M Tree objects (1M × 16 bytes = 16MB)
// Total: ~26MB instead of 2,000GB!
In The Wild (Real World Examples)
1. String Pool in Java
The most famous flyweight!
String s1 = "Hello"; // Goes to string pool
String s2 = "Hello"; // Reuses from pool
System.out.println(s1 == s2); // true (same object!)
2. Integer.valueOf() Cache
Java caches integers from -128 to 127:
Integer a = Integer.valueOf(100);
Integer b = Integer.valueOf(100);
System.out.println(a == b); // true (cached)
Integer c = Integer.valueOf(200);
Integer d = Integer.valueOf(200);
System.out.println(c == d); // false (not cached, different objects)
3. Font Rendering
GUI frameworks share font objects:
Font arial12 = FontFactory.getFont("Arial", 12);
// Same font object used for all "Arial 12" text
When to Use Flyweight
✅ Use when:
- Large number of similar objects
- Most state can be extrinsic (pulled out)
- Object identity doesn’t matter
❌ Don’t use when:
- Few objects
- No shared state
- Object identity is important (need unique instances)
Cheat Sheet
| Feature | Details |
|---|---|
| Category | Structural |
| Problem Solved | Memory overhead from large numbers of objects |
| Key implementation | Factory pool + intrinsic/extrinsic state separation |
| Pros | Memory savings (massive reduction for many objects) |
| Cons | Complexity (state separation), CPU tradeoff (some overhead) |
Tips to Remember 🧠
- “String Pool”: The #1 example every Java developer knows.
- “Intrinsic = Inside, Extrinsic = External”: Intrinsic is shared inside flyweight, extrinsic is passed from outside.
- “Factory Pattern”: Flyweight almost always uses a factory to manage the pool.
Advertisement
Moshiour Rahman
Software Architect & AI Engineer
Enterprise software architect with deep expertise in financial systems, distributed architecture, and AI-powered applications. Building large-scale systems at Fortune 500 companies. Specializing in LLM orchestration, multi-agent systems, and cloud-native solutions. I share battle-tested patterns from real enterprise projects.
Related Articles
The Visitor Design Pattern: Add Operations Without Modifying Classes
Master the Visitor Pattern in Java. Learn how to add new operations to object structures using double dispatch.
JavaThe Template Method Pattern: The Recipe for Success
Master the Template Method Pattern in Java. Learn how to define the skeleton of an algorithm in a superclass but let subclasses override specific steps.
JavaThe Strategy Design Pattern: Kill the If-Else Statements
Master the Strategy Pattern in Java. Learn how to replace complex if-else logic with interchangeable algorithms and follow the Open/Closed Principle.
Comments
Comments are powered by GitHub Discussions.
Configure Giscus at giscus.app to enable comments.