Flyweight Pattern: Saving Memory with Shared Objects
By Misbahul Munir3 min read583 words

Flyweight Pattern: Saving Memory with Shared Objects

Backend Development
design pattern
structural pattern

What is the Flyweight Pattern?

The Flyweight Pattern is a structural pattern used to minimize memory usage by sharing common data between similar objects, instead of creating duplicate instances.

In essence:

  • Separate the intrinsic state (shared and constant) from the extrinsic state (unique and variable).
  • Reuse shared parts for multiple objects to reduce memory usage.

Real-World Analogy: Text Editor Fonts

In a word processor:

  • You might have thousands of characters displayed.
  • But instead of storing font data (
    Arial
    ,
    12pt
    ,
    bold
    ) for every character, you store it once and reference it.

Each character holds:

  • Its position (extrinsic)
  • A reference to shared font formatting (intrinsic)

This is Flyweight in action.


Use Case in Backend Development

Imagine a logging service or analytics system:

  • Millions of events flow in.
  • Each has
    event_type
    ,
    timestamp
    ,
    user_id
    , and other fields.

Many events share the same

event_type
, e.g.,
"article_view"
,
"player_start"
, etc.

If we store these as full strings repeatedly, memory explodes. But if we reuse the same object for common event types, we save a ton of space.


Python Example: Using Flyweight for Event Types

Let’s implement this step by step.


Step 1: The Flyweight Class

This will hold the shared/intrinsic data (e.g.,

event_type
,
category
).

class EventType: def __init__(self, name: str, category: str): self.name = name self.category = category def __str__(self): return f"EventType(name={self.name}, category={self.category})"

Step 2: The Flyweight Factory

Ensures that only one instance per event type is created.

class EventTypeFactory: _event_types = {} @classmethod def get_event_type(cls, name: str, category: str) -> EventType: key = (name, category) if key not in cls._event_types: cls._event_types[key] = EventType(name, category) return cls._event_types[key]

Step 3: Event Class (with Extrinsic State)

class Event: def __init__(self, user_id: str, timestamp: str, event_type: EventType): self.user_id = user_id # extrinsic self.timestamp = timestamp # extrinsic self.event_type = event_type # intrinsic (shared) def __str__(self): return f"[{self.timestamp}] {self.user_id} - {self.event_type.name}"

Step 4: Simulating Event Logging

if __name__ == "__main__": factory = EventTypeFactory() # Shared EventTypes article_view_type = factory.get_event_type("article_view", "content") player_start_type = factory.get_event_type("player_start", "media") # Many events share the same type events = [ Event("user123", "2025-08-07T12:00", article_view_type), Event("user456", "2025-08-07T12:05", article_view_type), Event("user789", "2025-08-07T12:10", player_start_type), ] for event in events: print(event) # Prove that shared instances are reused print(f"Total EventType objects created: {len(factory._event_types)}")

Output:

[2025-08-07T12:00] user123 - article_view [2025-08-07T12:05] user456 - article_view [2025-08-07T12:10] user789 - player_start Total EventType objects created: 2

Despite having three events, we only created two

EventType
objects — thanks to the Flyweight pattern.


Benefits

  • Performance Boost: Less memory and faster processing for large datasets.
  • Cleaner Architecture: Separation of shared vs. unique data.
  • Lower Memory Footprint: Especially useful in games, UIs, logs, and analytics.

Drawbacks

  • Adds complexity in managing shared state
  • May not help much if most objects are unique
  • Needs clear boundaries between intrinsic and extrinsic data

When to Use Flyweight

  • Logging millions of events with few event types
  • UI systems with many similar objects (e.g., text, buttons, icons)
  • Game development: Reusing objects like enemies, particles, tiles
  • Backend services that stream repetitive sensor or metric data

Wrapping Up

The Flyweight Pattern is a perfect choice when you're scaling up and need to deal with a high volume of objects that share common internal data.

While it's not always necessary, it's crucial in performance-sensitive or memory-constrained systems, such as real-time logging, IoT processing, and data pipelines.