- Shift Elevate
- Posts
- Flyweight Pattern: Optimise Memory Usage with Shared Object Instances
Flyweight Pattern: Optimise Memory Usage with Shared Object Instances
The Pain of Memory Intensive Object Creation
Picture this: You're building a text editor that needs to display thousands of characters in a document. Your initial approach seems logical: create a new Character object for every letter typed, storing font information, size, weight, and rendering data.
Down the line, your application consumes 500MB of RAM just to display a 50,000 character document. Each character object stores the letter, font family, font size, weight, and rendering properties. With users typing long documents, you're creating thousands of objects when most characters share identical font information.
Suddenly, you're dealing with slow rendering, high memory usage, and performance bottlenecks that make your text editor unusable for large documents. Sound familiar?
The Flyweight pattern solves this exact challenge by sharing common object state across multiple instances, dramatically reducing memory usage while maintaining the same functionality.
Understanding the Flyweight Pattern
The Flyweight pattern minimises memory usage by sharing as much data as possible with similar objects. It stores intrinsic state (shared data) separately from extrinsic state (unique data), allowing multiple objects to share the same intrinsic state.
Think of it like a text editor: instead of storing font information with every character, you store the font data once and reference it from each character. The character only stores its unique position and color, while sharing letter, font family, and size information with similar characters.
This pattern promotes Memory Efficiency, Performance, and Scalability while maintaining object oriented design principles.
🔍 Intrinsic vs Extrinsic State: The Heart of Flyweight
The Flyweight pattern's power comes from understanding and separating two types of object state:
Definition: Data that can be shared across multiple flyweight instances
Characteristics:
Independent of the flyweight's context
Immutable once created
Stored inside the flyweight object
Same for many instances
Text Editor Example: Letter ('A'), Font family ("Arial"), Font size (12pt)
Memory Impact: Stored once, referenced by many instances
Extrinsic State (Unique Data)
Definition: Data that varies with each flyweight's context and cannot be shared
Characteristics:
Depends on the flyweight's specific context
Changes frequently
Passed to flyweight methods as parameters
Unique to each instance
Text Editor Example: Position (row, column)
Memory Impact: Stored separately for each instance
Important Distinction: Font vs Position
Font Properties (Intrinsic): The font family and size - part of the font definition that can be shared
Position Data (Extrinsic): Dynamic positioning data - unique per character instance
The Key Insight
❌ Without Flyweight: Each character = Letter + Font Family + Font Size + Position
✅ With Flyweight:
• Shared: Letter + Font Family + Font Size (stored once)
• Unique: Position (stored per instance)

Flyweight Pattern Components
Core Components
Flyweight Interface: Defines the interface for character objects that can receive and act on extrinsic state
Intrinsic State: The shared data that can be reused across multiple flyweight instances (font information)
Extrinsic State: The unique data that varies with each flyweight's context (position)
Concrete Flyweight: Implements the flyweight interface and stores intrinsic state (letter, font information)
Flyweight Factory: Creates and manages character flyweight objects, ensuring they are shared properly
Document Character: Holds the extrinsic state and maintains a reference to the shared flyweight
Complete Java Implementation
Let's build a text editor that demonstrates the Flyweight pattern's power in optimising memory usage for character rendering and document processing.
Flyweight Interface
public interface Character {
void display(Position position, String color, boolean bold);
char getLetter();
FontInfo getFontInfo();
}
Intrinsic State
public class FontInfo {
private String fontFamily;
private int fontSize;
public FontInfo(String fontFamily, int fontSize) {
this.fontFamily = fontFamily;
this.fontSize = fontSize;
}
// Getters
public String getFontFamily() { return fontFamily; }
public int getFontSize() { return fontSize; }
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
FontInfo other = (FontInfo) obj;
return Objects.equals(fontFamily, other.fontFamily) &&
fontSize == other.fontSize;
}
@Override
public int hashCode() {
return Objects.hash(fontFamily, fontSize);
}
}
Extrinsic State
public class Position {
private int row;
private int column;
public Position(int row, int column) {
this.row = row;
this.column = column;
}
public int getRow() { return row; }
public int getColumn() { return column; }
public Position withOffset(int rowOffset, int columnOffset) {
return new Position(row + rowOffset, column + columnOffset);
}
@Override
public String toString() {
return String.format("[%d,%d]", row, column);
}
}
Concrete Flyweight
public class CharacterFlyweight implements Character {
private char letter;
private FontInfo fontInfo;
public CharacterFlyweight(char letter, FontInfo fontInfo) {
this.letter = letter;
this.fontInfo = fontInfo;
}
@Override
public void display(Position position, String color, boolean bold) {
String boldText = bold ? " (bold)" : "";
System.out.printf("'%c' (%s, %dpt) at %s in %s%s%n",
letter, fontInfo.getFontFamily(), fontInfo.getFontSize(),
position, color, boldText);
}
@Override
public char getLetter() {
return letter;
}
@Override
public FontInfo getFontInfo() {
return fontInfo;
}
}
Flyweight Factory
public class CharacterFactory {
private Map<String, Character> characterCache = new HashMap<>();
public Character getCharacter(char letter, FontInfo fontInfo) {
String key = letter + "_" + fontInfo.hashCode();
return characterCache.computeIfAbsent(key, k -> {
System.out.println("Creating new character: '" + letter + "' (" + fontInfo.getFontFamily() + ")");
return new CharacterFlyweight(letter, fontInfo);
});
}
public int getCacheSize() {
return characterCache.size();
}
public void clearCache() {
characterCache.clear();
}
}
Document Character (Extrinsic State)
public class DocumentCharacter {
private Character character;
private Position position;
private String color;
private boolean bold;
public DocumentCharacter(Character character, Position position, String color, boolean bold) {
this.character = character;
this.position = position;
this.color = color;
this.bold = bold;
}
public void display() {
character.display(position, color, bold);
}
public Character getCharacter() { return character; }
public Position getPosition() { return position; }
public String getColor() { return color; }
public boolean isBold() { return bold; }
}
Client
public class TextEditor {
private List<DocumentCharacter> document = new ArrayList<>();
private CharacterFactory characterFactory;
public TextEditor(CharacterFactory characterFactory) {
this.characterFactory = characterFactory;
}
public void addCharacter(char letter, Position position, String color, boolean bold, FontInfo fontInfo) {
Character character = characterFactory.getCharacter(letter, fontInfo);
document.add(new DocumentCharacter(character, position, color, bold));
}
public void displayDocument() {
System.out.println("=== Document Content ===");
for (DocumentCharacter docChar : document) {
docChar.display();
}
System.out.println("Total characters in document: " + document.size());
System.out.println("Unique character objects: " + characterFactory.getCacheSize());
}
}
Expected Output
When you run the Flyweight pattern implementation, you'll see how character objects are shared efficiently:
Creating new character: 'H' (Arial)
Creating new character: 'H' (Times New Roman)
=== Document Content ===
'H' (Arial, 12pt) at [0,0] in black
'H' (Times New Roman, 14pt) at [1,0] in red (bold)
Total characters in document: 2
Unique character objects: 2
🚀 Get the Complete Implementation
The full code with advanced flyweight implementations and memory profiling is available in our Design Patterns Repository.
# Clone and run the complete demo
git clone https://github.com/shift-elevate/design-patterns.git
cd design-patterns
mvn test -Dtest=FlyweightPatternTest
Real World Examples
The Flyweight pattern is extensively used in real world applications:
1. Web Browsers
Browsers use flyweight patterns for DOM element rendering, where CSS styles are shared across multiple elements with the same styling properties. Font rendering systems also use flyweights to optimize memory usage.
2. Financial Trading Platforms
Real time trading platforms use flyweight patterns for stock quote processing, where company information is shared across thousands of price updates while maintaining unique price, volume, and timestamp data.
3. Database Systems
Database systems use flyweight patterns for connection pooling, where connection configurations are shared across multiple database connections.
When to Use Flyweight Pattern
Understanding when to apply the Flyweight pattern is crucial for making the right architectural decisions. Here's when it shines and when alternatives might be better:
✅ Ideal Scenarios:
You have a large number of similar objects that consume significant memory (e.g., thousands of characters in a document).
Most object state can be made extrinsic (stored externally) like position, color, formatting.
You can identify groups of objects that share common state (e.g., font information, character properties).
The application doesn't depend on object identity.
You need to optimise memory usage in performance critical applications like text editors or game engines.
❌ Skip It When:
The objects don't share enough common state to make sharing worthwhile.
The extrinsic state is complex and difficult to manage.
You need to maintain object identity for business logic.
The memory savings are negligible compared to implementation complexity.
Next Steps: Apply Flyweight Pattern in Your Project
Ready to implement the Flyweight pattern in your own projects? Here's a structured approach to get you started:
Identify Memory Intensive Objects: Look for objects that are created frequently and consume significant memory (e.g., characters in documents, UI elements, game objects).
Separate Intrinsic and Extrinsic State: Determine which properties can be shared (font info, character properties) and which must be unique (position, color, formatting state).
Create Flyweight Factory: Build a factory that manages shared instances and prevents duplicate creation.
Implement Flyweight Interface: Define the interface that allows extrinsic state to be passed to flyweight objects.
Test Memory Optimisation: Measure memory usage before and after implementation to validate improvements.
The Flyweight pattern transforms memory intensive applications into efficient, scalable systems. By sharing common object state, you can dramatically reduce memory consumption while maintaining the same functionality and performance—essential for text editors, game engines, and any application dealing with large numbers of similar objects.
Found this helpful? Share it with a colleague who's struggling with memory intensive object creation in text editors or document processing. Have questions about implementing Flyweight pattern in your specific use case? Email us directly, we read every message and the best questions become future newsletter topics.