Introduction
The Flyweight Pattern is a structural design pattern focused on minimizing memory usage by sharing instances of objects that are expensive to create and are used in large numbers.
Why Flyweight Pattern Matters
Imagine you're developing a text editor that renders millions of characters. If each character object holds formatting info (font, style, color), the memory usage would be massive. The Flyweight Pattern helps by reusing common data across multiple objects.
Core Intent and Participants
- Intent: Share objects to support a large number of fine-grained objects efficiently.
Participants
Flyweight
: The interface shared by all flyweight objects.ConcreteFlyweight
: Implements the Flyweight interface and stores intrinsic state.FlyweightFactory
: Creates and manages flyweight objects.Client
: Maintains extrinsic state and references flyweights.
UML Diagram (Text)
+------------------+
| Flyweight |
+------------------+
| + operation() |
+------------------+
▲
|
+----------------------+ +-------------------+
| ConcreteFlyweight |<-----| FlyweightFactory |
+----------------------+ +-------------------+
| + getFlyweight() |
+---------+ uses +-----------+
| Client |----->| Flyweight |
+---------+ +-----------+
Real-World Use Cases
- Text rendering engines (characters share font/style info)
- Particle systems in games
- Caching database connections or images
- Icons in UI apps (multiple buttons using the same icon)
- Tree structures in syntax trees
Java Implementation Strategy
Example: Drawing Trees in a Forest
Step 1: Flyweight Interface
public interface TreeType {
void display(int x, int y);
}
Step 2: Concrete Flyweight
public class ConcreteTreeType implements TreeType {
private String name;
private String color;
private String texture;
public ConcreteTreeType(String name, String color, String texture) {
this.name = name;
this.color = color;
this.texture = texture;
}
public void display(int x, int y) {
System.out.println("Drawing " + name + " tree at (" + x + ", " + y + ") with color " + color);
}
}
Step 3: Flyweight Factory
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 + "-" + texture;
if (!treeTypes.containsKey(key)) {
treeTypes.put(key, new ConcreteTreeType(name, color, texture));
}
return treeTypes.get(key);
}
}
Step 4: Client Class
public class Tree {
private int x, y; // extrinsic state
private TreeType type; // shared intrinsic state
public Tree(int x, int y, TreeType type) {
this.x = x;
this.y = y;
this.type = type;
}
public void draw() {
type.display(x, y);
}
}
Step 5: Demo
import java.util.ArrayList;
import java.util.List;
public class Forest {
public static void main(String[] args) {
List<Tree> trees = new ArrayList<>();
TreeType pine = TreeFactory.getTreeType("Pine", "Green", "Rough");
TreeType oak = TreeFactory.getTreeType("Oak", "Brown", "Smooth");
trees.add(new Tree(1, 1, pine));
trees.add(new Tree(2, 3, pine));
trees.add(new Tree(5, 5, oak));
for (Tree tree : trees) {
tree.draw();
}
}
}
✅ Only two TreeType objects are created and reused across multiple Tree instances.
Pros and Cons
✅ Pros
- Saves memory by sharing common objects
- Reduces object creation overhead
- Improves performance for large-scale object sets
❌ Cons
- Makes code more complex due to separation of intrinsic/extrinsic state
- Less flexibility in storing state within shared objects
- Debugging shared state issues can be tricky
Anti-Patterns and Misuse
- Using Flyweight for objects that rarely repeat (no benefit)
- Mixing intrinsic and extrinsic data inside the Flyweight (breaks pattern)
- Over-caching in the factory, leading to memory leaks
Flyweight vs Singleton vs Object Pool
Pattern | Purpose | Sharing Scope | Object Lifecycle Control | Real-World Use Case |
---|---|---|---|---|
Flyweight | Share many instances' data | Many-to-many | Managed by factory | Fonts, icons, text rendering |
Singleton | Ensure one instance per class | Global | Single instance | Logger, config |
Object Pool | Reuse a limited pool of objects | One-to-few | Checkout/checkin model | DB connections, threads |
Refactoring Legacy Code
Before
new ConcreteTreeType("Pine", "Green", "Rough") // Every time
After (Using Flyweight Factory)
TreeFactory.getTreeType("Pine", "Green", "Rough") // Shared
✅ Reduces memory and improves performance.
Best Practices
- Separate intrinsic (shared) and extrinsic (per-object) data
- Use factories for object reuse
- Combine with lazy loading and caching mechanisms
- Avoid premature optimization—profile first
Real-World Analogy
Imagine a car rental agency. Instead of giving everyone a new car every time, they maintain a limited set of vehicles (shared objects) and rent them to different users (extrinsic state). This is Flyweight in real life.
Java Version Relevance
- Java 8+: Use
Map.computeIfAbsent()
for cleaner factories - Java 11+: Enhanced performance helps object caching strategies
- Java 17+: Use sealed types to restrict Flyweight implementations
Conclusion & Key Takeaways
- Flyweight is a memory-saving pattern used in large-scale, repetitive object usage.
- Share intrinsic state and manage unique data separately.
- Use factory classes to efficiently manage instance reuse.
- Perfect for UI elements, text processing, games, and more.
FAQ – Flyweight Pattern in Java
1. What is the Flyweight Pattern?
A structural pattern to reduce memory by sharing common parts of objects.
2. When should I use Flyweight?
When your application has thousands or millions of similar objects.
3. How does it differ from Object Pool?
Object Pool manages object lifecycle. Flyweight shares immutable data.
4. Can Flyweight have mutable state?
No. It should only store immutable, shared state.
5. What’s intrinsic vs extrinsic data?
Intrinsic: shared (e.g., font), Extrinsic: unique (e.g., position)
6. How does it help performance?
It minimizes object creation and reduces memory usage.
7. Is it thread-safe?
Flyweights should be immutable to ensure thread safety.
8. Can I use it with Spring Beans?
Yes, with prototype-scope beans managed manually via factory.
9. Does Java use Flyweight internally?
Yes, in Integer.valueOf()
, String.intern()
, Boolean.TRUE/FALSE
10. Is it suitable for modern Java apps?
Yes, especially in graphics-heavy or high-performance apps.