Skip to content Skip to footer

Flyweight Design Pattern: Optimizing Memory Usage

You’re building a text editor that needs to render thousands of characters on screen. Each character could be an object with font, size, color, and style properties. Creating thousands of these objects would consume massive amounts of memory. The Flyweight pattern solves this by sharing common state between objects, dramatically reducing memory usage.

Problem

Consider a game that renders thousands of trees:

public class Tree
{
    public int X { get; set; }
    public int Y { get; set; }
    public string Type { get; set; }
    public string Color { get; set; }
    public string Texture { get; set; }
    public int Height { get; set; }
    public int Width { get; set; }

    public void Render()
    {
        Console.WriteLine($"Rendering {Type} tree at ({X}, {Y})");
    }
}

Creating 10,000 trees means 10,000 objects, each storing type, color, texture, height, and width—even though many trees share the same values. This wastes memory.

Solution

The Flyweight pattern minimizes memory usage by sharing as much data as possible with similar objects. It splits object state into:

  • Intrinsic state: Shared, immutable data (can be stored in flyweight)
  • Extrinsic state: Unique, context-specific data (passed as parameters)

Definition: The Flyweight pattern lets you fit more objects into the available amount of RAM by sharing common parts of state between multiple objects instead of keeping all the data in each object.

Pseudocode

FlyweightFactory
    ↓ creates/manages
Flyweight (shared intrinsic state)
    ↓ used by
Context (stores extrinsic state, references flyweight)

Examples

Here’s a complete, runnable C# implementation:

using System;
using System.Collections.Generic;

// Flyweight interface
public interface ICharacter
{
    void Display(int fontSize, string color);
}

// Concrete flyweight - stores intrinsic (shared) state
public class Character : ICharacter
{
    private char _symbol; // Intrinsic state

    public Character(char symbol)
    {
        _symbol = symbol;
    }

    // Extrinsic state passed as parameters
    public void Display(int fontSize, string color)
    {
        Console.WriteLine($"Character: {_symbol}, Size: {fontSize}, Color: {color}");
    }
}

// Flyweight factory
public class CharacterFactory
{
    private Dictionary<char, Character> _characters = new();

    public Character GetCharacter(char symbol)
    {
        if (!_characters.ContainsKey(symbol))
        {
            _characters[symbol] = new Character(symbol);
            Console.WriteLine($"Creating character: {symbol}");
        }
        else
        {
            Console.WriteLine($"Reusing character: {symbol}");
        }
        return _characters[symbol];
    }

    public int GetCharacterCount()
    {
        return _characters.Count;
    }
}

// Usage
class Program
{
    static void Main()
    {
        var factory = new CharacterFactory();
        string text = "HELLO WORLD";

        Console.WriteLine($"Rendering text: {text}\n");

        foreach (char c in text)
        {
            if (c != ' ')
            {
                var character = factory.GetCharacter(c);
                character.Display(12, "black");
            }
        }

        Console.WriteLine($"\nTotal unique characters created: {factory.GetCharacterCount()}");
        Console.WriteLine($"Total characters in text: {text.Replace(" ", "").Length}");
    }
}

Output:

Rendering text: HELLO WORLD

Creating character: H
Character: H, Size: 12, Color: black
Creating character: E
Character: E, Size: 12, Color: black
Creating character: L
Character: L, Size: 12, Color: black
Reusing character: L
Character: L, Size: 12, Color: black
Creating character: O
Character: O, Size: 12, Color: black
Reusing character: O
Character: O, Size: 12, Color: black
Creating character: W
Character: W, Size: 12, Color: black
Reusing character: O
Character: O, Size: 12, Color: black
Reusing character: R
Character: R, Size: 12, Color: black
Reusing character: L
Character: L, Size: 12, Color: black
Reusing character: D
Character: D, Size: 12, Color: black

Total unique characters created: 7
Total characters in text: 10

Real-World Example: Tree Rendering

// Intrinsic state (shared)
public class TreeType
{
    public string Name { get; }
    public string Color { get; }
    public string Texture { get; }

    public TreeType(string name, string color, string texture)
    {
        Name = name;
        Color = color;
        Texture = texture;
    }

    public void Draw(int x, int y)
    {
        Console.WriteLine($"Drawing {Name} tree ({Color}, {Texture}) at ({x}, {y})");
    }
}

// Flyweight factory
public class TreeFactory
{
    private Dictionary<string, TreeType> _treeTypes = new();

    public TreeType GetTreeType(string name, string color, string texture)
    {
        string key = $"{name}_{color}_{texture}";
        
        if (!_treeTypes.ContainsKey(key))
        {
            _treeTypes[key] = new TreeType(name, color, texture);
            Console.WriteLine($"Creating new tree type: {key}");
        }
        
        return _treeTypes[key];
    }

    public int GetTreeTypeCount()
    {
        return _treeTypes.Count;
    }
}

// Context - stores extrinsic state
public class Tree
{
    private TreeType _treeType; // Reference to flyweight
    public int X { get; set; }  // Extrinsic state
    public int Y { get; set; }  // Extrinsic state

    public Tree(TreeType treeType, int x, int y)
    {
        _treeType = treeType;
        X = x;
        Y = y;
    }

    public void Draw()
    {
        _treeType.Draw(X, Y);
    }
}

// Usage
var factory = new TreeFactory();

// Create tree types (shared)
var oakType = factory.GetTreeType("Oak", "Green", "Rough");
var pineType = factory.GetTreeType("Pine", "Dark Green", "Smooth");

// Create many trees (extrinsic state varies)
var trees = new List<Tree>
{
    new Tree(oakType, 10, 20),
    new Tree(oakType, 15, 25),
    new Tree(pineType, 20, 30),
    new Tree(oakType, 25, 35),
    new Tree(pineType, 30, 40)
};

Console.WriteLine($"\nCreated {trees.Count} trees using {factory.GetTreeTypeCount()} tree types\n");

foreach (var tree in trees)
{
    tree.Draw();
}

Applications

Enterprise Use Cases:

  1. Text Editors: Sharing character objects (font, style) while varying position
  2. Game Development: Sharing sprite/texture data for repeated game objects
  3. GUI Applications: Sharing button styles, icons, and fonts
  4. Document Processing: Sharing formatting information (fonts, styles) across many text elements
  5. Web Browsers: Sharing CSS styles across many DOM elements
  6. Database Connection Pooling: Sharing connection configurations
  7. Caching Systems: Sharing cached data objects
  8. Icon Systems: Sharing icon graphics while varying size and color

Benefits:

  • Dramatically reduces memory usage when many similar objects exist
  • Allows more objects to fit in available memory
  • Improves performance by reducing object creation overhead
  • Centralizes shared state management

When to Use:

  • Your application uses a large number of objects
  • Storage costs are high because of the sheer quantity of objects
  • Most object state can be made extrinsic
  • Groups of objects may be replaced by relatively few shared objects
  • The application doesn’t depend on object identity

Memory Savings Example:

  • Without Flyweight: 10,000 trees × 200 bytes = 2 MB
  • With Flyweight: 3 tree types × 200 bytes + 10,000 positions × 8 bytes = ~80 KB
  • Savings: ~96% reduction in memory usage

Key Insight: The Flyweight pattern is about sharing. Identify what’s common (intrinsic) and what’s unique (extrinsic). Store intrinsic state in flyweight objects, pass extrinsic state as parameters. This dramatically reduces memory usage when dealing with many similar objects.

Leave a Comment