Skip to content

Flyweight Design Pattern (Structural)

Flyweight is a structural design pattern that lets you fit more objects into the available amount of RAM by sharing common parts of state between multiple objects instead of keeping all of the data in each object.


Imagine you are building a video game where thousands of particles (like bullets or sparks) are rendered on the screen at once. Each particle contains specific data: its coordinates, vector, speed, and a large sprite image.

While coordinates and vectors change constantly, the sprite and color are often the same for all particles of a certain type. If you store the image data inside every single particle object, the game will quickly run out of memory and crash.

The Flyweight pattern suggests that you stop storing the intrinsic state (data that is constant and shared) inside each object. Instead, you move this state into a separate β€œFlyweight” object that multiple original objects can reference.

The extrinsic state (data that changes, like coordinates) stays with the original object (the β€œcontext”). This allows you to create millions of context objects that all point to just a few flyweight objects containing the heavy data.

  • Intrinsic State: Lives within the flyweight. It is constant and shareable (e.g., a tree’s texture and color).
  • Extrinsic State: Lives outside the flyweight. It is unique and changes based on context (e.g., a tree’s specific $(x, y)$ coordinates).

In a forest, you might see thousands of trees. Each tree has its own location on the ground. However, the β€œlook” of a specific species of tree (the shape of the leaves, the texture of the bark) is shared. Instead of every tree having its own unique β€œdesign plan,” nature uses one genetic blueprint for the species and applies it to different locations.


  1. Flyweight: Contains the portion of the state that can be shared between multiple objects.
  2. Context: Contains the extrinsic state, unique across all original objects.
  3. Flyweight Factory: Manages a pool of existing flyweights. It ensures that flyweights are shared and not duplicated.
  4. Client: Calculates or stores the extrinsic state and passes it to the flyweight methods.
classDiagram
    class FlyweightFactory {
        -flyweights: Flyweight[]
        +getFlyweight(sharedState)
    }
    class Flyweight {
        -intrinsicState
        +operation(extrinsicState)
    }
    class Context {
        -extrinsicState
        -flyweight: Flyweight
        +operation()
    }

    FlyweightFactory o-- Flyweight
    Context o-- Flyweight
    Client --> FlyweightFactory
    Client --> Context

# The Flyweight stores a portion of the state of a tree.
# These fields store values that are unique for each
# particular tree.
class TreeType:
field name, color, texture
constructor TreeType(name, color, texture) { ... }
method draw(canvas, x, y):
# 1. Create a bitmap of a given type, color & texture.
# 2. Draw the bitmap on the canvas at X and Y.
# Flyweight Factory decides whether to re-use existing
# flyweight or create a new object.
class TreeFactory:
static field treeTypes: set of TreeType
static method getTreeType(name, color, texture):
type = treeTypes.find(name, color, texture)
if (type == null):
type = new TreeType(name, color, texture)
treeTypes.add(type)
return type
# The Context object contains the extrinsic part of the state.
class Tree:
field x, y
field type: TreeType
constructor Tree(x, y, type) { ... }
method draw(canvas):
type.draw(canvas, this.x, this.y)

  • Memory Constraints: Use the Flyweight pattern only when your program must spawn a huge number of objects which barely fit into available RAM.
  • Redundant State: Use it when the objects contain a large amount of duplicate state that can be extracted and shared.
  1. Divide the fields of a class that will become a flyweight into two parts: intrinsic (constant) and extrinsic (contextual).
  2. Leave the intrinsic state in the class, but make it immutable.
  3. Create a new context class for the extrinsic state. It should contain a reference to the flyweight object.
  4. Create a factory that manages the pool of flyweights.
  5. Clients must use the factory to request flyweights instead of instantiating them directly.

Pros:

  • You can save a massive amount of RAM, assuming your program has many similar objects.

Cons:

  • You might be trading RAM for CPU cycles if the extrinsic data has to be recalculated every time it’s used.
  • The code becomes much more complicated.
  • Composite: Flyweight can be used to implement shared leaf nodes in a Composite tree to save RAM.
  • Facade: Flyweight explains how to create lots of small objects, while Facade shows how to create a single object that represents an entire subsystem.
  • Singleton: Flyweight resembles Singleton if you somehow manage to reduce all shared states of the objects to just one singleton object.

// Flyweight
public class TreeType
{
public string Name { get; }
public string Color { get; }
public TreeType(string name, string color)
{
Name = name;
Color = color;
}
public void Draw(int x, int y)
{
Console.WriteLine($"Draw {Name} ({Color}) in ({x}, {y})");
}
}
// Flyweight Factory
public class TreeFactory
{
private static readonly Dictionary<string, TreeType> _types = new();
public static TreeType GetTreeType(string name, string color)
{
string key = $"{name}-{color}";
if (!_types.ContainsKey(key))
_types[key] = new TreeType(name, color);
return _types[key];
}
}
// Context
public class Tree
{
private readonly int _x;
private readonly int _y;
private readonly TreeType _type;
public Tree(int x, int y, TreeType type)
{
_x = x;
_y = y;
_type = type;
}
public void Draw() => _type.Draw(_x, _y);
}
// Usage
class Program
{
static void Main()
{
var trees = new List<Tree>();
for (int i = 0; i < 5; i++)
{
var type = TreeFactory.GetTreeType("Oak", "Green");
trees.Add(new Tree(i * 10, i * 5, type));
}
foreach (var tree in trees)
tree.Draw();
}
}