20 minlesson

Introduction to Flyweight Pattern

Introduction to Flyweight Pattern

Flyweight uses sharing to support large numbers of fine-grained objects efficiently.

The Problem

Storing full address data for millions of shipments:

typescript
1// Each shipment stores full address details
2class Shipment {
3 origin: {
4 city: string; // "New York"
5 state: string; // "NY"
6 country: string; // "United States"
7 countryCode: string; // "US"
8 timezone: string; // "America/New_York"
9 };
10 destination: {
11 city: string;
12 state: string;
13 country: string;
14 countryCode: string;
15 timezone: string;
16 };
17}
18
19// 1 million shipments * ~500 bytes = 500MB of duplicated data
20// "New York, NY, United States" repeated thousands of times

The Solution: Flyweight

Share common data between objects:

typescript
1// Flyweight: shared, immutable state
2class CityInfo {
3 constructor(
4 public readonly city: string,
5 public readonly state: string,
6 public readonly country: string,
7 public readonly countryCode: string,
8 public readonly timezone: string
9 ) {}
10}
11
12// Flyweight Factory: creates and caches flyweights
13class CityInfoFactory {
14 private cache = new Map<string, CityInfo>();
15
16 get(city: string, state: string, country: string): CityInfo {
17 const key = `${city}|${state}|${country}`;
18
19 if (!this.cache.has(key)) {
20 this.cache.set(key, new CityInfo(
21 city,
22 state,
23 country,
24 this.getCountryCode(country),
25 this.getTimezone(city, state)
26 ));
27 }
28
29 return this.cache.get(key)!;
30 }
31
32 private getCountryCode(country: string): string { /* lookup */ }
33 private getTimezone(city: string, state: string): string { /* lookup */ }
34}
35
36// Shipment now references shared flyweights
37class Shipment {
38 constructor(
39 public origin: CityInfo, // Shared reference
40 public destination: CityInfo, // Shared reference
41 public trackingNumber: string, // Unique to this shipment
42 public weight: number // Unique to this shipment
43 ) {}
44}
45
46// 1 million shipments * ~50 bytes + ~1000 cities * 500 bytes = 50MB

Pattern Structure

1┌─────────────────────┐
2│ FlyweightFactory │
3│─────────────────────│
4│ - flyweights: Map │
5│ + getFlyweight(key) │
6└──────────┬──────────┘
7 │ creates/returns
8
9┌─────────────────────┐
10│ Flyweight │
11│─────────────────────│
12│ - intrinsicState │ ← Shared, immutable
13│ + operation(extr) │ ← Uses extrinsic state
14└─────────────────────┘
15
16 │ references
17┌──────────┴──────────┐
18│ Client │
19│─────────────────────│
20│ - extrinsicState │ ← Unique per context
21│ - flyweight │ ← Shared reference
22└─────────────────────┘

Key Concepts

Intrinsic State (Shared)

  • Stored in the flyweight
  • Independent of context
  • Immutable and shareable

Extrinsic State (Unique)

  • Stored by the client
  • Varies by context
  • Passed to flyweight operations
typescript
1class CityInfo {
2 // Intrinsic: same for all NYC shipments
3 readonly city = "New York";
4 readonly state = "NY";
5 readonly timezone = "America/New_York";
6
7 formatAddress(
8 street: string, // Extrinsic: unique per address
9 postalCode: string // Extrinsic: unique per address
10 ): string {
11 return `${street}\n${this.city}, ${this.state} ${postalCode}`;
12 }
13}

When to Use Flyweight

  1. Large numbers of objects - Thousands or millions of similar objects
  2. Significant shared state - Most object state can be shared
  3. Identity not important - Objects don't need unique identity
  4. Extrinsic state computable - Can derive context from external data

Memory Savings Calculation

1Without Flyweight:
2- 100,000 orders
3- 500 bytes per address
4- Total: 50 MB
5
6With Flyweight:
7- 100,000 orders with references (8 bytes each)
8- 1,000 unique cities (500 bytes each)
9- Total: 800 KB + 500 KB = 1.3 MB
10
11Savings: 97%

Summary

Flyweight dramatically reduces memory usage when many objects share common data. The key is separating intrinsic (shared) from extrinsic (unique) state.