The Flyweight design pattern uses sharing to support large numbers of fine-grained objects efficiently.
C# code examples of the Flyweight design pattern is provided in 3 forms:
A visualization of the classes and objects participating in this pattern.
The classes and objects participating in this pattern include:
Character
)
CharacterA, CharacterB, ..., CharacterZ
)
CharacterFactory
)
FlyweightApp
)
This structural code demonstrates the Flyweight pattern in which a relatively small number of objects is shared many times by different clients.
using System;
using System.Collections.Generic;
namespace Flyweight.Structural
{
/// <summary>
/// Flyweight Design Pattern
/// </summary>
public class Program
{
public static void Main(string[] args)
{
// Arbitrary extrinsic state
int extrinsicstate = 22;
FlyweightFactory factory = new FlyweightFactory();
// Work with different flyweight instances
Flyweight fx = factory.GetFlyweight("X");
fx.Operation(--extrinsicstate);
Flyweight fy = factory.GetFlyweight("Y");
fy.Operation(--extrinsicstate);
Flyweight fz = factory.GetFlyweight("Z");
fz.Operation(--extrinsicstate);
UnsharedConcreteFlyweight fu = new
UnsharedConcreteFlyweight();
fu.Operation(--extrinsicstate);
// Wait for user
Console.ReadKey();
}
}
/// <summary>
/// The 'FlyweightFactory' class
/// </summary>
public class FlyweightFactory
{
private Dictionary<string, Flyweight> flyweights { get; set; } = new Dictionary<string, Flyweight>();
// Constructor
public FlyweightFactory()
{
flyweights.Add("X", new ConcreteFlyweight());
flyweights.Add("Y", new ConcreteFlyweight());
flyweights.Add("Z", new ConcreteFlyweight());
}
public Flyweight GetFlyweight(string key)
{
return ((Flyweight)flyweights[key]);
}
}
/// <summary>
/// The 'Flyweight' abstract class
/// </summary>
public abstract class Flyweight
{
public abstract void Operation(int extrinsicstate);
}
/// <summary>
/// The 'ConcreteFlyweight' class
/// </summary>
public class ConcreteFlyweight : Flyweight
{
public override void Operation(int extrinsicstate)
{
Console.WriteLine("ConcreteFlyweight: " + extrinsicstate);
}
}
/// <summary>
/// The 'UnsharedConcreteFlyweight' class
/// </summary>
public class UnsharedConcreteFlyweight : Flyweight
{
public override void Operation(int extrinsicstate)
{
Console.WriteLine("UnsharedConcreteFlyweight: " +
extrinsicstate);
}
}
}
This real-world code demonstrates the Flyweight pattern in which a relatively small number of Character objects is shared many times by a document that has potentially many characters.
using System;
using System.Collections.Generic;
namespace Flyweight.RealWorld
{
/// <summary>
/// Flyweight Design Pattern
/// </summary>
public class Program
{
public static void Main(string[] args)
{
// Build a document with text
string document = "AAZZBBZB";
char[] chars = document.ToCharArray();
CharacterFactory factory = new CharacterFactory();
// extrinsic state
int pointSize = 10;
// For each character use a flyweight object
foreach (char c in chars)
{
pointSize++;
Character character = factory.GetCharacter(c);
character.Display(pointSize);
}
// Wait for user
Console.ReadKey();
}
}
/// <summary>
/// The 'FlyweightFactory' class
/// </summary>
public class CharacterFactory
{
private Dictionary<char, Character> characters = new Dictionary<char, Character>();
public Character GetCharacter(char key)
{
// Uses "lazy initialization"
Character character = null;
if (characters.ContainsKey(key))
{
character = characters[key];
}
else
{
switch (key)
{
case 'A': character = new CharacterA(); break;
case 'B': character = new CharacterB(); break;
//...
case 'Z': character = new CharacterZ(); break;
}
characters.Add(key, character);
}
return character;
}
}
/// <summary>
/// The 'Flyweight' abstract class
/// </summary>
public abstract class Character
{
protected char symbol;
protected int width;
protected int height;
protected int ascent;
protected int descent;
protected int pointSize;
public abstract void Display(int pointSize);
}
/// <summary>
/// A 'ConcreteFlyweight' class
/// </summary>
public class CharacterA : Character
{
// Constructor
public CharacterA()
{
symbol = 'A';
height = 100;
width = 120;
ascent = 70;
descent = 0;
}
public override void Display(int pointSize)
{
this.pointSize = pointSize;
Console.WriteLine(symbol +
" (pointsize " + this.pointSize + ")");
}
}
/// <summary>
/// A 'ConcreteFlyweight' class
/// </summary>
public class CharacterB : Character
{
// Constructor
public CharacterB()
{
symbol = 'B';
height = 100;
width = 140;
ascent = 72;
descent = 0;
}
public override void Display(int pointSize)
{
this.pointSize = pointSize;
Console.WriteLine(this.symbol +
" (pointsize " + this.pointSize + ")");
}
}
// ... C, D, E, etc.
/// <summary>
/// A 'ConcreteFlyweight' class
/// </summary>
public class CharacterZ : Character
{
// Constructor
public CharacterZ()
{
symbol = 'Z';
height = 100;
width = 100;
ascent = 68;
descent = 0;
}
public override void Display(int pointSize)
{
this.pointSize = pointSize;
Console.WriteLine(this.symbol +
" (pointsize " + this.pointSize + ")");
}
}
}
The .NET optimized code demonstrates the same code as above but uses
more modern C# and .NET features.
Here is an elegant C# Flyweight solution.
namespace Flyweight.NetOptimized;
using System.Collections.Generic;
using static System.Console;
/// <summary>
/// Flyweight Design Pattern
/// </summary>
public class Program
{
public static void Main()
{
// Build document with text
var document = "AAZZBBZB";
var factory = new CharacterFactory();
// extrinsic state
int pointSize = 10;
// For each character use a flyweight object
foreach (char c in document)
{
var character = factory.GetCharacter(c);
character.Display(++pointSize);
}
// Wait for user
ReadKey();
}
}
/// <summary>
/// The 'FlyweightFactory' class
/// </summary>
public class CharacterFactory
{
private readonly Dictionary<char, Character> characters = [];
public Character GetCharacter(char key)
{
// Uses "lazy initialization"
Character character;
if (characters.TryGetValue(key, out Character? value))
{
character = value;
}
else
{
character = key switch
{
'A' => new CharacterA(),
'B' => new CharacterB(),
// ...
'Z' => new CharacterZ(),
_ => null!
};
characters.Add(key, character);
}
return character;
}
}
/// <summary>
/// The 'Flyweight' class
/// </summary>
public class Character
{
protected char symbol;
protected int width;
protected int height;
protected int ascent;
protected int descent;
public void Display(int pointSize) =>
WriteLine($"{symbol} (pointsize {pointSize})");
}
/// <summary>
/// A 'ConcreteFlyweight' class
/// </summary>
public class CharacterA : Character
{
// Constructor
public CharacterA()
{
symbol = 'A';
height = 100;
width = 120;
ascent = 70;
descent = 0;
}
}
/// <summary>
/// A 'ConcreteFlyweight' class
/// </summary>
public class CharacterB : Character
{
// Constructor
public CharacterB()
{
symbol = 'B';
height = 100;
width = 140;
ascent = 72;
descent = 0;
}
}
// ... C, D, E, etc.
/// <summary>
/// A 'ConcreteFlyweight' class
/// </summary>
public class CharacterZ : Character
{
// Constructor
public CharacterZ()
{
symbol = 'Z';
height = 100;
width = 100;
ascent = 68;
descent = 0;
}
}