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:
typescript1// Each shipment stores full address details2class 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}1819// 1 million shipments * ~500 bytes = 500MB of duplicated data20// "New York, NY, United States" repeated thousands of times
The Solution: Flyweight
Share common data between objects:
typescript1// Flyweight: shared, immutable state2class 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: string9 ) {}10}1112// Flyweight Factory: creates and caches flyweights13class CityInfoFactory {14 private cache = new Map<string, CityInfo>();1516 get(city: string, state: string, country: string): CityInfo {17 const key = `${city}|${state}|${country}`;1819 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 }2829 return this.cache.get(key)!;30 }3132 private getCountryCode(country: string): string { /* lookup */ }33 private getTimezone(city: string, state: string): string { /* lookup */ }34}3536// Shipment now references shared flyweights37class Shipment {38 constructor(39 public origin: CityInfo, // Shared reference40 public destination: CityInfo, // Shared reference41 public trackingNumber: string, // Unique to this shipment42 public weight: number // Unique to this shipment43 ) {}44}4546// 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/returns8 ▼9┌─────────────────────┐10│ Flyweight │11│─────────────────────│12│ - intrinsicState │ ← Shared, immutable13│ + operation(extr) │ ← Uses extrinsic state14└─────────────────────┘15 ▲16 │ references17┌──────────┴──────────┐18│ Client │19│─────────────────────│20│ - extrinsicState │ ← Unique per context21│ - flyweight │ ← Shared reference22└─────────────────────┘
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
typescript1class CityInfo {2 // Intrinsic: same for all NYC shipments3 readonly city = "New York";4 readonly state = "NY";5 readonly timezone = "America/New_York";67 formatAddress(8 street: string, // Extrinsic: unique per address9 postalCode: string // Extrinsic: unique per address10 ): string {11 return `${street}\n${this.city}, ${this.state} ${postalCode}`;12 }13}
When to Use Flyweight
- Large numbers of objects - Thousands or millions of similar objects
- Significant shared state - Most object state can be shared
- Identity not important - Objects don't need unique identity
- Extrinsic state computable - Can derive context from external data
Memory Savings Calculation
1Without Flyweight:2- 100,000 orders3- 500 bytes per address4- Total: 50 MB56With 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 MB1011Savings: 97%
Summary
Flyweight dramatically reduces memory usage when many objects share common data. The key is separating intrinsic (shared) from extrinsic (unique) state.