🧱 Structural

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

RoleResponsibility
FlyweightContains the intrinsic state shared among multiple objects
FlyweightFactoryCreates and manages flyweight objects; ensures sharing by returning existing flyweights
ClientMaintains extrinsic state and passes it when invoking flyweight operations

TypeScript Example

TypeScript
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

More Structural Patterns

Explore All Design Patterns

Browse our complete reference of 23 design patterns with TypeScript examples, UML participants, and practical guidance.