This is the fifth post in the Structural Design Patterns series and in this post, we will discuss the Flyweight Design Pattern and its implementation using the C# programming language.
The Flyweight Design Pattern provides a way to minimize memory footprint when there is a need to create a large number of similar objects. The Flyweight Design Pattern falls into the Structural Design Pattern category as defined by the Gang of Four (GoF). If you are new to design patterns, I would highly recommend you to read the introductory post “Introduction to Design Patterns“
What is Flyweight Design Pattern?
The Flyweight Design Pattern provides a way to minimize the memory footprint by managing the object state (bifurcating the constant one) so that the common constant state can be shared across similar objects.
In general, a large number of objects uses a load of memory and the pattern helps to break (or isolate) the object properties so that they can be shared across the objects. The breaking of object properties is achieved by dividing them into two types viz: Intrinsic and Extrinsic.
The Intrinsic properties are those properties that remain constant across the objects and their value can be easily shared between all objects.
The Extrinsic properties are those properties whose values vary and can be unique across the objects.
The Flyweight Design Pattern as per the GOF book: “Use sharing to support large numbers of fine-grained objects efficiently”
When to consider Flyweight Design Pattern
Few of the scenarios when the Flyweight Design Pattern can be considered are as follows:
- The application uses a large number of objects (mostly identical in nature) and they are expensive to store.
- The application object has a load of memory footprint and drains away from the available RAM on the target device, in other words, the availability of RAM is a constraint.
- The object properties contain duplicate state (value) which can be easily isolated and shared between multiple objects.
- The identity of the object is not a matter of concern and the required operations can be performed on the shared objects.
What is Flyweight Design Pattern – UML
The artifacts of the UML diagram are as follows:
- Flyweight: This is the interface that defines the behavior for the Flyweight concrete classes to perform operations on the extrinsic properties (states). An abstract can also be used in place of the interface.
- ConcreteFlyweight: This class inherits from the Flyweight interface and provides the functionality and the stateless information that will be shared across the objects.
- UnsharedFlyweight: The UnsharedFlyweight inherits from the Flyweight interface and provides the possibility of creating a stateful object that is not shared.
- FlyweightFactory: This is the factory class that maintains a reference list of all Flyweight objects that have been created, and when the client demands a new object, first it is checked in the list, if the required object is found then it is returned to the client. If the requested object is not present, then a new one is created, added to the reference list and the same is returned to the client.
- Client: This is the consumer of the Flyweight objects and maintains a reference of all the objects it is using as well as it also computes and maintains the extrinsic state of the object.
C# Implementation
Let us build up a NET Core C# console application to implement the Flyweight Design Pattern using a simple example. The requirement of the application is defined as follows:
Requirement Description
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 the stars and the planet objects are as follow:
- All the stars and planets will be elliptical in shape and of the same size. Star will be of size 10×10 and the size of the planet will be 30×30.
- 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 requirement, let us try to fit the requirement using the Flyweight Design Pattern. Let us break Star and the planet object’s properties into Intrinsic and Extrinsic state first.
- Intrinsic Objects: These values are common across all the stars and planet objects and they are not going to change.
- Shape and size.
- The color of the Stars always is blue and the of a planet is red.
- 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.
- Position of the star and a planet.
- The brightness of the star and planet.
Requirement Implementation in C#
Let us implement the above-mentioned requirement using the C# programming language. All the artifacts of the application are as follows:
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); }
Ellipse.cs: This is the class that will represent the intrinsic state of the star and the planet objects.
public class Ellipse { private readonly int height; private readonly int width; private readonly string color; public Ellipse(int height, int width, string color) { this.height = height; this.width = width; this.color = color; } }
PlanetaryType: The enumeration defines the planetary types.
public enum PlanetaryType { 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 Ellipse StarShape = new Ellipse(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 at 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 Ellipse StarShape = new Ellipse(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 at [{positionX},{positionY}] coordinate and is shining with [{brightness}]% brightness"); } }
GalaxyFactory: This is the factory class that maintains the dictionary of Planetary object and creates a new one for the client if it does have a reference of it before returning it to the client.
public class GalaxyFactory { private static Dictionary<PlanetaryType, IGalaxy> planetoryObjects = new Dictionary<PlanetaryType, IGalaxy>(); public static IGalaxy GetPlanetoryObject(PlanetaryType planetoryObject) { if (planetoryObjects.ContainsKey(planetoryObject)) return planetoryObjects[planetoryObject]; else { IGalaxy NewObject = null; if (planetoryObject == PlanetaryType.Star) { NewObject = new Star(); planetoryObjects.Add(PlanetaryType.Star, NewObject); } else { NewObject = new Planet(); planetoryObjects.Add(PlanetaryType.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.GetPlanetoryObject(PlanetaryType.Star); star.SetBrightness(10); star.SetPosition(20, 80); Console.WriteLine(star); IGalaxy planet = GalaxyFactory.GetPlanetoryObject(PlanetaryType.Planet); planet.SetBrightness(67); planet.SetPosition(120, 85); Console.WriteLine(planet); IGalaxy star2 = GalaxyFactory.GetPlanetoryObject(PlanetaryType.Star); star2.SetBrightness(65); star2.SetPosition(67, 23); Console.WriteLine(star2); } }
Application Output
The output of the application is as follows:
Source Code
The source code for the console application is available at GitHub: https://github.com/technicalbundle/FlyweightDesignPattern
Advantages of Flyweight Design Pattern
A few of the advantages of the Flyweight Design Pattern are as follows:
- The Flyweight Pattern contributes to improving the performance of the application by reducing the number of objects.
- The Flyweight Pattern reduces the memory footprint and saving RAM as the common properties are shared between objects using Intrinsic properties.
Disadvantages of Flyweight Design Pattern
A few of the disadvantages of Flyweight Design Pattern are as follows:
- If there are no shareable properties in an object, the pattern is of no use.
- If memory is not a concern, implementing Flyweight design can be overkill for the application.
- The pattern introduces code complexity.
Conclusion
In this blog post, we try to learn about the Flyweight Design Pattern, a few scenarios where it can be applied along with its advantages and disadvantages, and also tried to implement the same with an example. The pattern really comes in handy in a situation where memory is a concern and the object of similar types need to created in large number.
I hope you found this post helpful. Thanks for visiting. Cheers!!!
[Further Readings: Composite Design Pattern in C# | Facade Design Pattern in C# | Proxy Design Pattern in C# | SQLite Studio to manage SQLite databases | Adapter Design Pattern in C# | How to use Blazor EditForm for Model Validation | How to add a new profile in Windows Terminal | How to easily Customize Global Settings in Windows Terminal | How to add Git Bash to Windows Terminal Application | How to customize Windows Terminal Application | How to customize Windows Terminal Key Bindings | How to Install Windows Terminal in Windows 10 ]