Type Structural
Purpose Use sharing to support large numbers of fine-grained objects efficiently.

Description

The Flyweight pattern is used when a system must support a very large number of similar objects and storing all of their state independently would be too expensive in memory. It reduces that overhead by separating shared state from unshared state, so that the common portion can be stored once and reused across many objects.

In Flyweight terminology, the shared state is called intrinsic state: information that is common and can be reused safely across contexts. The varying, context-specific state is called extrinsic state and is supplied from outside the flyweight when needed, rather than being stored permanently inside it.

By moving common data out of individual objects and into shared flyweight instances, the pattern can greatly reduce memory consumption in systems that would otherwise require large numbers of fine-grained objects.

Flyweight can also be viewed as a specialised variation on object creation patterns such as Factory, since flyweights are typically obtained through a factory-like mechanism that returns shared existing instances instead of always creating new ones.

Flyweight is primarily concerned with sharing efficiently to support large numbers of similar objects.


Overview

Flyweight
Client Responsible for storing and managing the unique or extrinsic state that belongs to each object. The client interacts with the Flyweight Factory to retrieve flyweight instances.
Flyweight Represents an interface or abstract class through which flyweight objects can receive and act on extrinsic state.
ConcreteFlyweight Implements the Flyweight interface and is capable of storing intrinsic (shared) state. Intrinsic state is independent of the ConcreteFlyweight's context and can be shared among all identical objects.
FlyweightFactory Creates and manages the Flyweight objects. It ensures that flyweights are shared properly. When a client requests a flyweight, the FlyweightFactory supplies an existing instance or creates one if it doesn't exist.

Implementation

Text editors such as LibreOffice are often cited as a classic use case for the Flyweight pattern, because they may need to represent extremely large numbers of characters within a document. With a naïve design, each character might be modelled as a separate object carrying its own font, colour, size, and other display information. In a large document, that duplication of state would become unnecessarily expensive in memory.

A flyweight-based design addresses this by separating the shared state from the context-specific state. Instead of storing all formatting information in every character object, the system creates shared flyweight objects representing reusable combinations such as a character together with its font, colour, and size — that is, the intrinsic state.

Each character occurrence in the document then stores only a reference to the appropriate flyweight, along with context-specific information such as its position in the document. That positional information is the extrinsic state, because it depends on where and how the character is being used rather than on the shared character representation itself.

This approach can significantly reduce memory consumption when the system must manage very large numbers of similar fine-grained objects.

// Client

public class TextCharacter
{
    // Reference to Intrinsic state 

    private TextCharacterFlyweight flyweight;
    
    // Extrinsic state

    private char character;
    private int positionX;
    private int positionY;

    public TextCharacter(
        char character, 
        string font, 
        int size, 
        Color color, 
        int x_position, 
        int y_position)
    {
        this.character = character;
        this.flyweight = FlyweightFactory.GetFlyweight(font, size, color);                
        this.positionX = x_position;
        this.positionY = y_position;
    }

    // Other methods...
}

// Flyweight

public class TextCharacterFlyweight
{
    // Shared state

    private string font; 
    private int size;
    private Color color;

    // Constructor and other methods...
}

// FlyweightFactory

public static class FlyweightFactory
{
    private static readonly 
        Dictionary<(string Font, int Size, Color Color), TextCharacterFlyweight> 
            flyweights = [];

    public static TextCharacterFlyweight GetFlyweight(string font, int size, Color color)
    {
        var key = (font, size, color);

        if (!flyweights.TryGetValue(key, out var flyweight))
        {
            flyweight = new TextCharacterFlyweight(font, size, color);
            flyweights[key] = flyweight;
        }

        return flyweights[key];
    }
}

Notes

Here is another simple example, in which:

Tree is the client that stores the extrinsic state (x, y, size) and a reference to a TreeType (the Flyweight) which represents the intrinsic state (name, color, texture).
TreeType is the Flyweight class that represents the shared intrinsic state.
TreeFactory serves as the factory that creates and manages TreeType instances.
Forest class uses the client objects (Tree), and acts as the context in which trees are created and manipulated.
// Client

public class Tree(double x, double y, double size, TreeType type)
{
    public void Draw() => type.Draw(x, y, size);
}

// Flyweight

public class TreeType(string name, string color, string texture)
{
    public void Draw(double x, double y, double size)
        => Console.WriteLine($"{size} meter {color} {texture} {name} tree at ({x}, {y})");
}

// Flyweight Factory

public static class TreeFactory
{
    private static readonly 
        Dictionary<(string Name, string Color, string Texture), TreeType> 
            TreeTypes = new();

    public static TreeType GetTreeType(string name, string color, string texture)
    {
        var key = (name, color, texture);

        if (!TreeTypes.TryGetValue(key, out var type))
        {
            type = new TreeType(name, color, texture);
            TreeTypes.Add(key, type);
        }

        return type;
    }
}

// Context 

public class Forest
{
    private readonly List<Tree> _trees = [];
    
    public void PlantTree(
        double x, 
        double y, 
        double size, 
        string name, 
        string color, 
        string texture)
    {
        var type = TreeFactory.GetTreeType(name, color, texture);
        var tree = new Tree(x, y, size, type);
        _trees.Add(tree);
    }

    public void Draw()
    {
        foreach (var tree in _trees)
        {
            tree.Draw();
        }
    }
}

// Demonstration

public static class FlyweightDemo
{
    public static void Execute()
    {
        var forest = new Forest();
        var random = new Random();

        // plant sixteen random trees

        for (var y = 1; y < 5; ++y)
        {
            for (var x = 1; x < 5; ++x)
            {
                var n = random.Next(10);
                
                var (name, color, texture) = n switch
                {
                    < 5 => ("Birch", "White", "Smooth"),
                    _ => ("Oak", "Brown", "Rough")
                };

                var size = 5 + n;
                
                forest.PlantTree(x, y, size, name, color, texture);
            }
        }

        forest.Draw();
    }

// outputs:

// 8 meter White Smooth Birch tree at (1, 1)
// 10 meter Brown Rough Oak tree at (2, 1)
// 6 meter White Smooth Birch tree at (3, 1)
// etc...

}

Notes

Due to the nature of software design, some elements of a pattern might not explicitly exist in different implementations. It's the overall concept and the goals of the pattern that matter — in this case, sharing to save resources. For example, Flyweight and ConcreteFlyweight might fold into the same class.

It's worth remembering this distinction:

  • The Intrinsic State is the data shared across multiple objects and is stored in the Flyweight.
  • The Extrinsic State is context-specific data that is not stored in the shared Flyweight. The client usually manages and supplies this state when invoking Flyweight operations.

Summary

Robert Nystrom describes the flyweight pattern here.

For more information, please see here.