When you develop applications that process a very large number of objects โ long lists, graph nodes, characters, repetitive structures โ memory consumption can quickly become a problem.
This is where the Flyweight Pattern comes in, a Structural Design Pattern that allows sharing of common objects, massively reducing allocated memory.
๐ What is the Flyweight Pattern?
Flyweight is a pattern that separates intrinsic state (which can be shared) from extrinsic state (specific to each object).
Thus, instead of creating thousands or millions of identical objects, you reuse already created instances.
It is especially useful in applications with:
-
many repeated graphic elements
-
large data lists
-
character-by-character texts
-
cache scenarios
-
document or map generation
๐ง Concept in brief
Objects are divided into:
-
Intrinsic state โ properties common to all instances
-
Extrinsic state โ values specific to each usage
The key thing: only the extrinsic state is passed at runtime, and intrinsic objects are reused.
๐ Structure of actors
-
Flyweight โ the interface used by all shared objects
-
ConcreteFlyweight โ reusable object containing intrinsic state
-
FlyweightFactory โ creates and manages common instances
-
Client โ provides extrinsic state and uses the flyweight
๐งช Practical example in C#
Scenario: we manage the characters of a text in an editor. The letters are identical in shape, differing only in their positions on the screen.
๐ฏ 1. Flyweight Interface
public interface ICharacterFlyweight
{
void Render(int fontSize, int x, int y);
}
๐ฏ 2. Concrete Flyweight (intrinsic state)
public class CharacterFlyweight : ICharacterFlyweight
{
private readonly char _symbol; // intrinsic
public CharacterFlyweight(char symbol)
{
_symbol = symbol;
}
public void Render(int fontSize, int x, int y)
{
Console.WriteLine($"Rendering '{_symbol}' at ({x},{y}) with size {fontSize}");
}
}
๐ฏ 3. Flyweight Factory (character cache)
public class CharacterFlyweightFactory
{
private readonly Dictionary<char, ICharacterFlyweight> _characters = new();
public ICharacterFlyweight GetFlyweight(char symbol)
{
if (!_characters.ContainsKey(symbol))
{
_characters[symbol] = new CharacterFlyweight(symbol);
Console.WriteLine($"Creating new flyweight for '{symbol}'");
}
return _characters[symbol];
}
}
๐ฏ 4. Using Flyweight in an application
var factory = new CharacterFlyweightFactory();
string text = "AAABBC";
int x = 0;
foreach (char c in text)
{
var character = factory.GetFlyweight(c);
character.Render(fontSize: 12, x: x, y: 10);
x += 10;
}
๐ Benefits
โ Reduced memory consumption
โ High scalability in repetitive structures
โ Performance improvement in graphic, map, cache scenarios
โ Disadvantages
โ Requires careful separation of intrinsic/extrinsic state
โ Increases code complexity
โ Can be over-engineering if the number of objects is not large
๐งญ When to use Flyweight?
Use it when:
-
you have very many similar objects
-
memory consumption becomes a problem
-
objects share a large part of behavior/state
-
you want a cache of reusable objects
Avoid it if:
-
objects are few
-
extrinsic state is complicated
-
differences between instances are large
๐ Conclusion
The Flyweight Pattern is essential for memory optimization in large, repetitive, or graphic applications. By separating states and reusing common objects, you can dramatically reduce the RAM footprint while maintaining the same functional behavior.
If your .NET application processes millions of elements, this pattern can make the difference between a slow and a performant system.