Flyweight design pattern is one of the structural patterns introduced by GOF. Flyweight pattern is about creating a pool of objects which allow sharing already created objects and causing applications to consume less memory.
So this pattern does two things, because the pattern creates objects only once and then saves it to pool
- Increase application performance in terms of object creation, as there is no requirement of creating an object at every request.
- Cause applications to consume less memory as created objects are already in memory and no new objected because of pool.
Ultimately flyweight patterns make applications efficient in terms of memory and processing.
Basic UML class Diagram of Design Pattern
The below image shows the class diagram of basic flyweight design pattern:
IFlyweight
- Basic contract that need to be implemented by derived types i.e. by concreate flyweight.
FlyweightFactory
– It’s a factory class which is used by Client class to get data from the concreate flyweight. This class is also responsible for creating a pool of already created objects. So when a request comes for data that is already requested, it returns data from pool.
ConcerateSharedFlyweight1
– It’s a concreate implementation of flyweight interface. As the name suggests, objects returned by this class are going to be shared between clients of it.
ConcerateUnSharedFlyweight1
– It’s a concreate implementation of flyweight interface. As the name suggests, objects returned by this class are going to be unshared, which means every time client requires a new object.
Client – make use of the concreate implementation, it creates an instance of Decorate and uses a functionality of it.
Note:
UnSharedFlyweight
is not always required it depends on requirement, but SharedFlyweight
is always required when you go for flyweight pattern.
The code below is implementation of the Flyweight design pattern and Class diagram discussed above.
namespace BasicFlyweightPattern
{
#region basic implementation
public class FlyweightFactory
{
public static List<Item> GetStaticItemList(string key)
{
IFlyWeight flyWeight = null;
ICacheManager _objCacheManager = CacheFactory.GetCacheManager();
if (key == "A")
{
if (_objCacheManager.Contains("A"))
{
return _objCacheManager["A"] as List<Item>;
}
else
{
flyWeight = new ConcerateSharedFlyweight1();
}
}
else if (key == "B")
{
if (_objCacheManager.Contains("B"))
{
return _objCacheManager["B"] as List<Item>;
}
else
{
flyWeight = new ConcerateSharedFlyweight2();
}
}
var list = flyWeight.GetList();
_objCacheManager.Add(key, list);
return list;
}
}
interface IFlyWeight
{
List<Item> GetList();
}
public class Item
{
public int id { get; set; }
public string desc { get; set; }
}
public class ConcerateSharedFlyweight1 : IFlyWeight
{
private List<Item> ItemList;
public ConcerateSharedFlyweight1()
{
ItemList = new List<Item>();
}
public List<Item> GetList()
{
ItemList.Add(new Item { id = 1, desc = "A1" });
ItemList.Add(new Item { id = 2, desc = "A2" });
ItemList.Add(new Item { id = 3, desc = "A3" });
return ItemList;
}
}
public class ConcerateSharedFlyweight2 : IFlyWeight
{
private List<Item> ItemList;
public ConcerateSharedFlyweight2()
{
ItemList = new List<Item>();
}
public List<Item> GetList()
{
ItemList.Add(new Item { id = 1, desc = "B1" });
ItemList.Add(new Item { id = 2, desc = "B2" });
ItemList.Add(new Item { id = 3, desc = "B3" });
return ItemList;
}
}
class Program
{
static void Main(string[] args)
{
List<Item> list =FlyweightFactory.GetStaticItemList("A");
foreach (var item in list)
{
Console.WriteLine(item.id.ToString() + " " + item.desc );
}
Console.ReadLine();
}
}
}
Points to remember in above code implementation
- Client asks for data from
FlyweightFactory
by calling GetStaticItemList
and passing key as argument for getting data. FlyweightFactory
has static method GetStaticItemList
, which creates instances of concreate and get data if request comes first time, for later request it returns existing created object. FlyweightFactory
maintains pool of already created objects by making use of Enterprise library. Caching block for sharing data between different clients i.e. different objects. - In Concreate implementation of flyweight, a hardcoded list is returned but in actual scenarios this will be replaced by getting data from datasource.
- Item is a class that is poco class, list of which returned from flyweight concreate classes. This class represented by different items. The below scenario provides more detail.
Output
Here in output shows result of A’s same will go for result of B’s. If call comes second time than data get retuned from cache.
Flyweight Design Pattern Example In RealTime Application
Problem Statement
Web Application having dropdown for displaying list of Country, displaying list of State, displaying list of Product etc. in dropdown and this dropdowns are part of multiple screen which access by multiple users.
In this scenario to display a different kind of list for multiple user request, the server needs to connect with the database server multiple items this reduce performance of application and also consumes memory for creating and storing these lists.
Solution
Solution to above problem solution is making use of Flyweight design pattern.
Below is a class diagram of Flyweight pattern used in application.
Mapping with Basic Implementation
IFlyweightManager
is equals to IFlyweight
StaticDataListFlyweightFactory
is equal to FlyweightFactory
.
CountryStaticListManager
& ProductStaticListManager
is equal to ConcerateSharedFlyweight1
& ConcerateSharedFlyweight2
.
StaticItem
is equal to Item.
namespace FlyWeightPattern
{
class Program
{
static void Main(string[] args)
{
List<StaticItem> countrylist = StaticDataListFlyWeidhtFactory.GetStaticItemList("Country");
foreach (var item in countrylist)
{
Console.WriteLine(item.id.ToString() + " " + item.Code + " " + item.Description);
}
Console.ReadLine();
}
}
public class StaticDataListFlyWeidhtFactory
{
public static List<StaticItem> GetStaticItemList(string key)
{
IFlyWeightManager manager = null;
ICacheManager _objCacheManager = CacheFactory.GetCacheManager();
if (key == "Country")
{
if (_objCacheManager.Contains("Country"))
{
return _objCacheManager["Country"] as List<StaticItem>;
}
else
{
manager = new CountryStaticListManager();
}
}
else if (key == "ProductType")
{
if (_objCacheManager.Contains("ProductType"))
{
return _objCacheManager["ProductType"] as List<StaticItem>;
}
else
{
manager = new ProductTypeStaticListManager();
}
}
var list = manager.GetList();
_objCacheManager.Add(key, list);
return list;
}
}
interface IFlyWeightManager
{
List<StaticItem> GetList();
}
public class CountryStaticListManager : IFlyWeightManager
{
private List<StaticItem> StaticItemList;
public CountryStaticListManager()
{
StaticItemList = new List<StaticItem>();
}
public List<StaticItem> GetList()
{
StaticItemList.Add(new StaticItem { id = 1, Code = "IND", Description = "India" });
StaticItemList.Add(new StaticItem { id = 2, Code = "SRL", Description = "Sri Lanka" });
StaticItemList.Add(new StaticItem { id = 3, Code = "SA", Description = "South Africa" });
return StaticItemList;
}
}
public class ProductTypeStaticListManager : IFlyWeightManager
{
private List<StaticItem> StaticItemList;
public ProductTypeStaticListManager()
{
StaticItemList = new List<StaticItem>();
}
public List<StaticItem> GetList()
{
StaticItemList.Add(new StaticItem { id = 1, Code = "0123", Description = "Watch" });
StaticItemList.Add(new StaticItem { id = 2, Code = "0234", Description = "Shoes" });
StaticItemList.Add(new StaticItem { id = 3, Code = "0345", Description = "" });
return StaticItemList;
}
}
public class StaticItem
{
public int id { get; set; }
public string Code { get; set; }
public string Description { get; set; }
}
}
Output
So the above code works the same as already described in the Basic implementation of the pattern. In code, different types of the list get fetched by passing different keys. For this implementation, the keys are Country and Product. In a real application, one can have more than this.
The above implementation follows SOLID principle. One exception is Factory class, which needs to be modifying when one wants to add a new key so it breaks the rule of Single responsibility.
Note:
Here, country and product are listed are hardcoded, but in actuality it gets fetched from the database.
Conclusion
This pattern is very helpful in scenarios where one wants to create a pool of objects and share it between different clients (clients are software programs or classes or web applications, as in this example).
Note:
This is my point of view regarding pattern. Please provide your feedback regarding it and also provide feedback if you find something wrong.