Flyweight Structural Design Pattern

Smita Nangare
Globant
Published in
6 min readDec 1, 2022
Photo by Joel Fulgencio on Unsplash

When there is a need to create many objects of similar nature, the Flyweight pattern is used. It provides a solution to reduce memory footprints and increase performance by sharing objects, as many objects consume a large amount of memory.

The Pattern Itself

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

Below are the in-use examples that relate to this design pattern.

  • We can use an image in multiple places on a webpage in browsers. Browsers will load the image only one time, and at other times browsers will reuse the image from the cache. Now the image is the same but used in multiple places. Its URL is an intrinsic attribute because it's fixed and shareable. The image's position coordinates, height, and width are extrinsic attributes that vary according to the context where they have to be rendered.
  • In .NET, we can relate the Flyweight design pattern with the intern pool maintained by CLR (Common Language Runtime), which contains the literal strings in a program. This ensures that repeated use of the same constant strings in our code will use the same string reference. The System.String class provides an Intern method that ensures a string is in the intern pool and returns its reference.

Flyweight in UML

The following UML diagram represents the Flyweight Design Pattern.

UML Diagram of the Flyweight Design Pattern

As per the UML diagram of the Flyweight Design Pattern, we can find below a description of the terms used in this pattern.

  • The Flyweight interface enables sharing but does not enforce it as concrete objects that implement this interface, either shared or unshared.
  • The ConcreteFlyweight class implements the Flyweight interface and adds storage for the intrinsic state.
  • The unsharedConcreteFlyweight class also implements the Flyweight interface and adds storage for instances that are not shared.
  • The FlyweightFactory has the GetFlyweight method, and we have to pass the key to this method. Whether the flyweight object is in the cache or not will be checked based on the key. If it is there, then it will return that existing flyweight object. And if it is not there, it will create a new flyweight object, add that object to the cache, and return that flyweight object.

Following are the states of the Flyweight Design Pattern.

  • Intrinsic: These are the things that are constant and stored in memory.
  • Extrinsic: These things are not constant and need to be calculated on the fly and, therefore, not stored in the memory.

An example in C#

Following is the cache representation of the example.

Cache Representation of the Example.

Assuming we are building a small gaming application to represent stars and planets in the galaxy. Here every star and planet will be positioned by the client application during the play. The characteristics of stars and the planets are as follow:

  • All stars and planets will be astronomical objects.
  • The color of the stars will be blue and that of the planet will be red.
  • The position and the brightness of the stars will be provided by the client during the runtime.
  • The client can create a large number of stars and planet objects.
  • The application will be used in a mobile application and memory (RAM) is a major constraint.

As per the requirement, let us first combine the star and planet object’s properties into Intrinsic and Extrinsic states.

  • Intrinsic Objects: These values are common across all the stars and planet objects and will not change. The color of the stars always is blue and the of a planet is red, and their shapes are fixed.
  • Extrinsic Objects: These are the values that will be provided by the client during runtime and will be unique across the star and planet objects. We will work with the position and brightness of stars and planets.

IGalaxy.cs: This is an interface and defines the method for manipulating the extrinsic state of the objects.

public interface IGalaxy
{
void SetBrightness(double brightness);
void SetPosition(int x, int y);
}

AstronomicalObject.cs: This is the class that will represent the intrinsic state of the star and the planet objects.

public class AstronomicalObject
{
private readonly int height;
private readonly int width;
private readonly string color;
public AstronomicalObject(int height, int width, string color)
{
this.height = height;
this.width = width;
this.color = color;
}
}

AstronomicalObjectType: The enumeration defines the astronomical object types.

public enum AstronomicalObjectType
{
Star,
Planet
}

Planet.cs: This is the class that represents the planet object and implements the IGalaxy interface to manipulate the extrinsic states.

 public class Planet : IGalaxy
{
public static AstronomicalObject PlanetShape = new AstronomicalObject(30, 30, "red"); // Intrinsic State
// These are the extrinsic states
int positionX;
int positionY;
double brightness;
public void SetBrightness(double brightness)
{
this.brightness = brightness;
}
public void SetPosition(int x, int y)
{
positionX = x;
positionY = y;
}
public override string ToString()
{
return string.Format($"A Planet located at [{positionX},{positionY}] coordinate and is having a brigtness of [{brightness}]%");
}
}

Star.cs: This is the class that represents the star object and also implements the IGalaxy interface to allow the client to manipulate the extrinsic state like brightness and the star location.

  public class Star : IGalaxy
{
public static AstronomicalObject StarShape = new AstronomicalObject(10, 10, "blue"); // Intrinsic State
// These are the extrinsic states
int positionX;
int positionY;
double brightness;
public void SetBrightness(double brightness)
{
this.brightness = brightness;
}
public void SetPosition(int x, int y)
{
positionX = x;
positionY = y;
}
public override string ToString()
{
return string.Format($"A Star located at [{positionX},{positionY}] coordinate and is shining with [{brightness}]% brightness");
}
}

GalaxyFactory: This is the factory class that maintains the dictionary of the Planetary object and creates a new one for the client if it has a reference before returning it to the client.

 public class GalaxyFactory
{
private static Dictionary<AstronomicalObjectType, IGalaxy> astronomicalObjects = new Dictionary<AstronomicalObjectType, IGalaxy>();
public static IGalaxy GetAstronomicalObject(AstronomicalObjectType planetoryObject)
{
if (astronomicalObjects.ContainsKey(planetoryObject))
return astronomicalObjects[planetoryObject];
else
{
IGalaxy NewObject = null;
if (planetoryObject == AstronomicalObjectType.Star)
{
NewObject = new Star();
astronomicalObjects.Add(AstronomicalObjectType.Star, NewObject);
}
else
{
NewObject = new Planet();
astronomicalObjects.Add(AstronomicalObjectType.Planet, NewObject);
}
return NewObject;
}
}
}

Client.cs: This is the client application that uses the GalaxyFactory and IGalaxy interface to interact with the objects.

class Client
{
static void Main(string[] args)
{
IGalaxy star = GalaxyFactory.GetAstronomicalObject(AstronomicalObjectType.Star);
star.SetBrightness(10);
star.SetPosition(20, 80);
Console.WriteLine(star);
IGalaxy planet = GalaxyFactory.GetAstronomicalObject(AstronomicalObjectType.Planet);
planet.SetBrightness(67);
planet.SetPosition(120, 85);
Console.WriteLine(planet);
IGalaxy star2 = GalaxyFactory.GetAstronomicalObject(AstronomicalObjectType.Star);
star2.SetBrightness(65);
star2.SetPosition(67, 23);
Console.WriteLine(star2);
}
}

Output

Below is the output as per our code implementation.

Application Output

When to use the Flyweight Design Pattern

Below are some reasons why we can use this design pattern.

  • Many similar objects are needed, and the storage cost is high.
  • Most of the state can be kept on disk or calculated at runtime.
  • The majority of each object's state data can be made extrinsic.
  • A few shared objects would easily replace many unshared objects.
  • The identity of each object does not matter.

The following are the pros and cons of this pattern, which also help decide when and if to use it.

Pros

  • The Flyweight Pattern improves the application's performance by reducing the number of objects.
  • The Flyweight Pattern reduces the memory footprint and saves RAM as the common properties are shared between objects using intrinsic properties.

Cons

  • The pattern is of no use if an object has no shareable properties.
  • If memory is not a concern, implementing the Flyweight design can be overkill for the application.
  • The pattern introduces code complexity.

Conclusion

This article taught us how the Flyweight Design Pattern increases performance and decreases memory footprints by sharing similar objects.

--

--