| 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
| 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.