Flyweight Pattern
Uses sharing to support large numbers of fine-grained objects efficiently. Flyweight reduces memory usage by sharing as much data as possible with similar objects, storing shared state (intrinsic) externally from unique state (extrinsic).
✦ Problem
Your application creates a huge number of similar objects that consume all available memory. For example, a text editor creating a separate object for each character on screen, or a game creating objects for each tree, bullet, or particle. Most of each object's state can be shared, but the direct approach stores it redundantly in every instance.
✦ Solution
Separate the object's state into intrinsic (shared) and extrinsic (unique) portions. Intrinsic state lives inside the flyweight and is immutable. Extrinsic state is passed to flyweight methods by the client. A factory manages a pool of flyweights, returning existing ones when available instead of creating new objects.
Participants
| Role | Responsibility |
|---|---|
| Flyweight | Contains the intrinsic state shared among multiple objects |
| FlyweightFactory | Creates and manages flyweight objects; ensures sharing by returning existing flyweights |
| Client | Maintains extrinsic state and passes it when invoking flyweight operations |
TypeScript Example
class IconStyle {
constructor(
public readonly icon: string,
public readonly color: string,
public readonly size: number,
) {}
render(x: number, y: number): string {
return `${this.icon} at (${x},${y}) color=${this.color} size=${this.size}`;
}
}
class IconFactory {
private pool = new Map<string, IconStyle>();
getIcon(icon: string, color: string, size: number): IconStyle {
const key = `${icon}-${color}-${size}`;
if (!this.pool.has(key)) {
this.pool.set(key, new IconStyle(icon, color, size));
}
return this.pool.get(key)!;
}
get count() { return this.pool.size; }
}
const factory = new IconFactory();
const markers: Array<{ style: IconStyle; x: number; y: number }> = [];
for (let i = 0; i < 1000; i++) {
const style = factory.getIcon("📍", "red", 16);
markers.push({ style, x: Math.random() * 1000, y: Math.random() * 1000 });
}
for (let i = 0; i < 500; i++) {
const style = factory.getIcon("🏠", "blue", 20);
markers.push({ style, x: Math.random() * 1000, y: Math.random() * 1000 });
}
console.log(`1500 markers using only ${factory.count} style objects`);
console.log(markers[0].style.render(markers[0].x, markers[0].y));Real-World Example
Text editors use Flyweight for character formatting. Instead of storing font, size, and color for each of millions of characters, the editor stores shared formatting objects and references them. String interning in Java and .NET pools identical strings. Browsers share CSS computed styles across identical DOM elements. Game engines use flyweights for repeated textures, particle systems, and tile data.
✓ When to Use
- •Your application uses a huge number of objects that would consume too much memory
- •Most object state can be made extrinsic (calculated or stored outside the object)
- •Many groups of objects can be replaced by relatively few shared objects
- •The application doesn't depend on object identity because flyweights are shared
✗ When Not to Use
- •The number of objects is small and memory savings are negligible
- •Objects have significant unique state that can't be externalized
- •The complexity of managing extrinsic state outweighs the memory savings
Understanding the Flyweight Pattern
The Flyweight pattern is one of the most important structural design patterns in object-oriented software design. Originally cataloged by the Gang of Four (Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides) in their seminal 1994 book "Design Patterns: Elements of Reusable Object-Oriented Software," this pattern continues to be a cornerstone of modern software architecture.
Uses sharing to support large numbers of fine-grained objects efficiently. Flyweight reduces memory usage by sharing as much data as possible with similar objects, storing shared state (intrinsic) externally from unique state (extrinsic). The pattern involves the following key participants: Flyweight, FlyweightFactory, Client. Each participant has a well-defined role, and the interactions between them create a flexible, maintainable structure that can evolve with changing requirements.
In TypeScript and modern JavaScript development, the Flyweight pattern is particularly valuable because the language's type system and interface support allow you to express the pattern with compile-time safety. TypeScript generics can make Flyweight implementations even more flexible and reusable while maintaining strong type checking throughout the pattern's structure.
Knowing when to apply the Flyweight pattern is as important as knowing how. Common use cases include: Your application uses a huge number of objects that would consume too much memory; Most object state can be made extrinsic (calculated or stored outside the object); Many groups of objects can be replaced by relatively few shared objects. However, overusing design patterns can lead to unnecessarily complex code. Always evaluate whether the pattern genuinely solves your problem or if a simpler approach would suffice.
The Flyweight pattern is related to several other design patterns: Composite, Singleton, State, Strategy. Understanding these relationships helps you choose the right pattern for your specific situation and combine patterns effectively when building complex systems.
Related Patterns
Composes objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objec…
Ensures a class has only one instance and provides a global point of access to it. The Singleton pattern restricts insta…
Allows an object to alter its behavior when its internal state changes. The object appears to change its class. State en…
Defines a family of algorithms, encapsulates each one, and makes them interchangeable. Strategy lets the algorithm vary …
More Structural Patterns
Explore All Design Patterns
Browse our complete reference of 23 design patterns with TypeScript examples, UML participants, and practical guidance.