Introduction
In this post, we will talk about Flyweight design pattern. We will see when one should use this design pattern and how we can implement it. In the below example, we will use C# language to implement the example.
Description
In early days of computing, memory was very costly, but nowadays, it's getting cheaper on a daily basis. Usually, in software application, memory is needed to create and hold objects. Sometimes, some objects stay in memory for a longer period of time. It's the developer’s responsibility to remove the object from memory whenever it's not needed to save memory. Another way to save memory is to use a created object instead of creating a new object each time. This will definitely save memory and improve the performance of the application.
To achieve this, we should have a pool of objects where we will keep all newly created objects. Whenever we need the same object, we will query to pool and get that object. After query, if the needed object doesn’t exist in the pool, we will create new one and store it in the pool. In a higher language like C# or Java, we can use Dictionary<Key,Value> or HashTable<Key,Value> to create a pool.
Example
Let's add new project. You can give any name you want to it.
Lets create a contract called “IShape” which will be implemented by different shapes.
- internal interface IShape
- {
- void Print();
- }
Lets add a “Rectangle” shape.
- internal class Rectangle : IShape
- {
- public void Print()
- {
- Console.WriteLine("Printing Rectangle");
- }
- }
Lets add a “Circle” shape.
- internal class Circle : IShape
- {
- public void Print()
- {
- Console.WriteLine("Printing Circle");
- }
- }
Lets add “Shapes” enum.
- public enum Shapes
- {
- Rectangle,
- Circle
- }
Here's a factory class which will hold all objects in dictionary (hashtable). If the requested object doesn't exist in this list, then it will create it, or else it will return the already created one.
- internal class ShapeObjectFactory
- {
- private readonly Dictionary<Shapes, IShape> shapes = new Dictionary<Shapes, IShape>();
-
- public int TotalObjectsCreated
- {
- get { return shapes.Count; }
- }
-
- public IShape GetShape(Shapes shapeType)
- {
- IShape shape = null;
- if (shapes.ContainsKey(shapeType))
- {
- shape = shapes[shapeType];
- }
- else
- {
- switch (shapeType)
- {
- case Shapes.Rectangle:
- shape = new Rectangle();
- shapes.Add(Shapes.Rectangle, shape);
- break;
-
- case Shapes.Circle:
- shape = new Circle();
- shapes.Add(Shapes.Circle, shape);
- break;
-
- default:
- throw new Exception("Factory cannot create the object specified");
- }
- }
- return shape;
- }
- }
Client program
- internal class Program
- {
- private static void Main(string[] args)
- {
- var factoryObject = new ShapeObjectFactory();
-
- IShape shape = factoryObject.GetShape(Shapes.Rectangle);
- shape.Print();
- shape = factoryObject.GetShape(Shapes.Rectangle);
- shape.Print();
- shape = factoryObject.GetShape(Shapes.Rectangle);
- shape.Print();
-
- shape = factoryObject.GetShape(Shapes.Circle);
- shape.Print();
- shape = factoryObject.GetShape(Shapes.Circle);
- shape.Print();
- shape = factoryObject.GetShape(Shapes.Circle);
- shape.Print();
-
- int NumObjs = factoryObject.TotalObjectsCreated;
- Console.WriteLine("\nTotal No of Objects created = {0}", NumObjs);
- Console.ReadKey();
- }
- }
Execution of simple program:
Conclusion
Advantages
- We should use this design pattern when we need to create large number objects.
- Reduces memory usage by sharing heavy objects.
Happy coding!