The Flyweight Pattern is a structural design pattern that minimizes memory usage by sharing as much data as possible between similar objects. Imagine a text editor rendering a document with thousands of characters. Each character needs a font, size, and color — but most characters share the same formatting. Instead of storing duplicate formatting data in every character object, the Flyweight Pattern extracts the shared state into reusable objects.
This pattern is critical in performance-sensitive applications where creating millions of similar objects would consume too much memory.
You are building a text editor that represents every character in a document as an object. Each character object stores its value, font family, font size, color, and position. A 100,000-character document creates 100,000 objects, most of which share the same font, size, and color. The application consumes gigabytes of memory for data that is largely duplicated.
Simply put, the naive approach stores redundant data thousands of times over, wasting memory that could be avoided.
Split the object’s data into two categories: intrinsic state (shared, immutable data like font and color) and extrinsic state (unique, context-specific data like position). Create flyweight objects that store only the intrinsic state and share them across all characters that have the same formatting. Pass the extrinsic state (position) as method parameters when needed.
A flyweight factory ensures that only one flyweight object exists per unique combination of intrinsic state.
The Flyweight Pattern applies the principle of sharing to support large numbers of fine-grained objects efficiently. It trades a small increase in code complexity for a massive reduction in memory consumption. The key distinction is between intrinsic state (shareable) and extrinsic state (unique per context).
Here we build a character rendering system where formatting is shared across characters.
import java.util.HashMap;
import java.util.Map;
// Flyweight: stores shared intrinsic state (font properties)
public class CharacterStyle {
private final String fontFamily;
private final int fontSize;
private final String color;
public CharacterStyle(String fontFamily, int fontSize, String color) {
this.fontFamily = fontFamily;
this.fontSize = fontSize;
this.color = color;
}
public void render(char character, int row, int col) {
System.out.printf("Rendering '%c' at (%d,%d) with %s %dpx %s%n",
character, row, col, fontFamily, fontSize, color);
}
public String getKey() {
return fontFamily + "_" + fontSize + "_" + color;
}
}
// Flyweight Factory: ensures shared instances
public class StyleFactory {
private final Map<String, CharacterStyle> styles = new HashMap<>();
public CharacterStyle getStyle(String fontFamily, int fontSize, String color) {
String key = fontFamily + "_" + fontSize + "_" + color;
if (!styles.containsKey(key)) {
System.out.println("[Factory] Creating new style: " + key);
styles.put(key, new CharacterStyle(fontFamily, fontSize, color));
}
return styles.get(key);
}
public int getStyleCount() {
return styles.size();
}
}
// Context: stores extrinsic state (position) + reference to flyweight
public class Character {
private final char value;
private final int row;
private final int col;
private final CharacterStyle style; // Shared flyweight
public Character(char value, int row, int col, CharacterStyle style) {
this.value = value;
this.row = row;
this.col = col;
this.style = style;
}
public void render() {
style.render(value, row, col);
}
}
// Usage
public class Main {
public static void main(String[] args) {
StyleFactory factory = new StyleFactory();
// Simulate a document with many characters sharing few styles
List<Character> document = new ArrayList<>();
// Body text: thousands of characters, all same style
String bodyText = "The Flyweight Pattern saves memory by sharing objects.";
CharacterStyle bodyStyle = factory.getStyle("Arial", 12, "#333333");
for (int i = 0; i < bodyText.length(); i++) {
document.add(new Character(bodyText.charAt(i), 0, i, bodyStyle));
}
// Heading: different style, shared among heading characters
String heading = "Design Patterns";
CharacterStyle headingStyle = factory.getStyle("Arial", 24, "#000000");
for (int i = 0; i < heading.length(); i++) {
document.add(new Character(heading.charAt(i), 1, i, headingStyle));
}
// Code snippet: yet another shared style
String code = "Map<String, Object> cache";
CharacterStyle codeStyle = factory.getStyle("Courier", 14, "#008000");
for (int i = 0; i < code.length(); i++) {
document.add(new Character(code.charAt(i), 2, i, codeStyle));
}
// Render first few characters
for (int i = 0; i < 5; i++) {
document.get(i).render();
}
System.out.println("\nTotal characters: " + document.size());
System.out.println("Unique styles (flyweights): " + factory.getStyleCount());
System.out.println("Memory saved by sharing styles instead of duplicating them");
}
}
The same character rendering flyweight scenario in Python.
from dataclasses import dataclass
# Flyweight: stores shared intrinsic state (font properties)
class CharacterStyle:
def __init__(self, font_family: str, font_size: int, color: str):
self.font_family = font_family
self.font_size = font_size
self.color = color
def render(self, character: str, row: int, col: int) -> None:
print(f"Rendering '{character}' at ({row},{col}) "
f"with {self.font_family} {self.font_size}px {self.color}")
@property
def key(self) -> str:
return f"{self.font_family}_{self.font_size}_{self.color}"
# Flyweight Factory: ensures shared instances
class StyleFactory:
def __init__(self):
self._styles: dict[str, CharacterStyle] = {}
def get_style(self, font_family: str, font_size: int, color: str) -> CharacterStyle:
key = f"{font_family}_{font_size}_{color}"
if key not in self._styles:
print(f"[Factory] Creating new style: {key}")
self._styles[key] = CharacterStyle(font_family, font_size, color)
return self._styles[key]
@property
def style_count(self) -> int:
return len(self._styles)
# Context: stores extrinsic state (position) + reference to flyweight
@dataclass
class Character:
value: str
row: int
col: int
style: CharacterStyle # Shared flyweight
def render(self) -> None:
self.style.render(self.value, self.row, self.col)
# Usage
if __name__ == "__main__":
factory = StyleFactory()
document: list[Character] = []
# Body text: many characters, all same style
body_text = "The Flyweight Pattern saves memory by sharing objects."
body_style = factory.get_style("Arial", 12, "#333333")
for i, ch in enumerate(body_text):
document.append(Character(ch, row=0, col=i, style=body_style))
# Heading: different style, shared among heading characters
heading = "Design Patterns"
heading_style = factory.get_style("Arial", 24, "#000000")
for i, ch in enumerate(heading):
document.append(Character(ch, row=1, col=i, style=heading_style))
# Code snippet: yet another shared style
code = "cache: dict[str, object]"
code_style = factory.get_style("Courier", 14, "#008000")
for i, ch in enumerate(code):
document.append(Character(ch, row=2, col=i, style=code_style))
# Render first few characters
for char in document[:5]:
char.render()
print(f"\nTotal characters: {len(document)}")
print(f"Unique styles (flyweights): {factory.style_count}")
print("Memory saved by sharing styles instead of duplicating them")
String.intern() — The JVM’s string pool is a flyweight implementation, sharing identical string instancesInteger.valueOf() — Caches Integer objects for values -128 to 127, returning shared instancesa = 100; b = 100; a is b return True