Design Patterns Flyweight
The Flyweight pattern saves memory by sharing common data among many similar objects. Instead of storing the same details a million times, the program stores them once and lets every object point to the shared copy.
The Real-World Analogy
A printing press owns one metal block for each letter. Pages of 3,000 letters need no 3,000 separate blocks. The press reuses the block for "e" every time that letter appears, and only the position on the page changes.
Problem Without Flyweight
Forest with 100,000 Tree objects Tree #1 [ name="Oak", texture=2 MB, color=green, x=10, y=40 ] Tree #2 [ name="Oak", texture=2 MB, color=green, x=55, y=12 ] Tree #3 [ name="Oak", texture=2 MB, color=green, x=71, y=90 ] ... Memory: 100,000 × 2 MB = 200,000 MB of mostly identical data
Solution With Flyweight
Shared (intrinsic) data Unique (extrinsic) data ┌──────────────────────┐ ┌───────────────┐ │ TreeType "Oak" │◀──────│ Tree x=10 y=40│ │ texture = 2 MB │◀──────│ Tree x=55 y=12│ │ color = green │◀──────│ Tree x=71 y=90│ └──────────────────────┘ └───────────────┘ Memory: one 2 MB texture + 100,000 tiny coordinate records
Intrinsic and Extrinsic State
Intrinsic State
Data that stays identical across objects. It lives inside the flyweight and gets shared. Tree name, texture, and color belong here.
Extrinsic State
Data that differs per object. The client stores it and passes it to the flyweight when needed. Position and size belong here.
Structure
┌─────────────────────┐ creates/reuses ┌───────────────────────┐
│ FlyweightFactory │───────────────────▶│ Flyweight │
│ - pool: Map │ │ - sharedData │
│ + get(key) │ │ + draw(extrinsic) │
└─────────────────────┘ └───────────────────────┘
▲
│ asks
┌────┴─────┐
│ Client │ keeps extrinsic data
└──────────┘
Code Example
class TreeType { // flyweight
private String name;
private String texture;
TreeType(String name, String texture) { ... }
void draw(int x, int y) {
print(name + " at " + x + "," + y);
}
}
class TreeFactory {
private static Map types = new HashMap();
static TreeType get(String name, String texture) {
if (!types.containsKey(name)) {
types.put(name, new TreeType(name, texture)); // build once
}
return types.get(name); // share after
}
}
class Tree {
private int x, y;
private TreeType type; // shared reference
Tree(int x, int y, TreeType type) { ... }
void draw() { type.draw(x, y); }
}
TreeType oak = TreeFactory.get("Oak", "oak.png");
Tree a = new Tree(10, 40, oak);
Tree b = new Tree(55, 12, oak); // same oak object reusedMemory Savings at a Glance
| Approach | Objects Holding Texture | Texture Memory |
|---|---|---|
| Without Flyweight | 100,000 | 200,000 MB |
| With Flyweight | 1 | 2 MB |
Where Flyweight Appears
- Text editors share glyph shapes for each character.
- Games share meshes and textures for crowds, grass, and bricks.
- Map applications share icon images across thousands of markers.
- Many languages cache small strings and numbers internally.
Advantages and Disadvantages
| Advantages | Disadvantages |
|---|---|
| Large memory savings | Code grows more complex |
| Faster loading of big scenes | Extrinsic data adds bookkeeping |
| Fewer objects for the garbage collector | Shared objects must stay unchangeable |
Step-by-Step Walkthrough
Walk through the forest code. The program asks TreeFactory for an Oak type. Checking its map, the factory finds nothing and builds a TreeType to store. A second request for Oak finds the stored object and returns the same reference.
Each Tree object keeps only coordinates and a pointer to its type. Ten thousand trees share a single TreeType, and memory use stays small.
Immutability Matters
Shared objects must never change after creation. One tree that recolors its shared type would recolor every other tree. Keep flyweights read-only, and send changing data through method parameters.
Common Mistakes
- Applying Flyweight to a few hundred objects. The savings rarely justify the extra code.
- Storing extrinsic data inside the shared object.
- Building flyweights directly with new, which bypasses the factory and loses sharing.
Practice Idea
Draw a text document with 5,000 characters on paper. Mark which data repeats for every letter "a" and which data changes per position. The repeated data forms the flyweight, and the positions form the extrinsic state.
Plain-Words Summary
Flyweight rewards patience and measurement. Profile the application, find the object type that appears in huge numbers, and inspect which fields repeat. Move those repeated fields into a shared object, and keep the rest in a small wrapper. Measure memory again to confirm the gain. Without measurement, the extra complexity may deliver nothing. Let numbers guide every decision.
Key Takeaways
- Flyweight shares identical data across many objects.
- Intrinsic state stays inside the shared object, and extrinsic state travels in from the client.
- A factory hands out existing flyweights instead of building new ones.
- Use it only when huge object counts cause real memory pressure.
