Monday, September 7, 2015

Flyweight Design Pattern

The Flyweight Design Pattern is one of the structural patterns introduced by the GOF. The Flyweight Pattern is about creating a pool of objects that allows sharing already existing objects and causing the application to consume less memory.

So this pattern does the following two things because the pattern creates objects once and then saves them to a pool:
  1. Increases application performance in terms of object creation since there is no requirement of creating an object for every request.
  2. Causes the application to consume less memory, since the object already exists in memory and no new object must be created because of the pool.
Ultimately flyweight patterns make the application efficient in terms of memory and processing.

Basic UML class Diagram of Design Pattern

The following image shows the class diagram of the basic Flyweight Design Pattern.



UML class Diagram
  • IFlyweight: Basic contract to be implemented by derived types, in other words by concrete flyweight.
  • FlyweightFactory: It's a factory class used by a client class to get the data from the concreate flyweight. This class is also responsible for creating the pool of an existing object. So when the request for data come that is already requested it returns data from the pool.
  • ConcerateSharedFlyweight1: It's a concreate implementation of the flyweight interface. As the name suggests, an object returned by this class will be shared among the clients of it.
  • ConcerateUnSharedFlyweight1: It's a concreate implementation of the flyweight interface. As the name suggests an object returned by this class will be unshared. That means that every time the client requires a new object.
  • Client: uses the concreate implementation, it creates an instance of Decorate and uses the functionality of it.
Note:UnSharedFlyweight is not always required, it depends on requirements, but SharedFlyweight is always required when you use the flyweight pattern.

The following code is the implementation of the Flyweight Design Pattern and the Class Diagram was shown 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");
     //List<Item> list = FlyweightFactory.GetStaticItemList("B");            
     foreach (var item in list)
            {
                Console.WriteLine(item.id.ToString() + "  " + item.desc );
           }

            Console.ReadLine();
        }
    }

}
      
The following are points to remember about the preceding code implementation:
  1. The client asks for data from FlyweightFactory by calling GetStaticItemList and passing the key as argument for getting the data.
  2. FlyweightFactory has a static method GetStaticItemList that creates an instance of concrete and gets data if the request is for the first time, for a later request it returns an existing object.
  3. FlyweightFacotry maintains a pool of existing objects using an Enterprise library caching block for sharing data among the clients, in other words different objects.
  4. In the concrete implementation of flyweight, a hardcoded list is returned but in an actual scenario this will be replaced by data from a data source.
  5. Item is a poco class, the list of which is returned from flyweight concrete classes. This class is represented by different items. The following actual scenario provides more detail.
Output



Here the output shows the result of A's same will use the result of B's. If the call comes a second time then the data is retuned from the cache.

Flyweight Design Pattern Example In an Actual Application

Problem Statement

A web application having a dropdown for displaying a list of Country, displaying a list of State, displaying a list of Product and so on in the dropdown and the dropdowns are part of multiple screens that are accessed by multiple users.

In this scenario, to display another kind of list for multiple user requests, the server must connect with the database server multiple times. This reduces the performance of the application and also consumes memory for creating and storing these lists.

Solution

The solution to the preceding problem is to use the Flyweight Design Pattern.

The following is a class diagram of the Flyweight pattern used in the application.


 
Mapping with Basic Implementation.
  • IFlyweightManager is equals to IFlyweight.
  • StaticDataListFlyweightFactory is equal to FlyweightFactory.
  • CountryStaticListManager and ProductStaticListManager is equal to ConcerateSharedFlyweight1 and 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 preceding code works the same as already described in the basic implementation of pattern. In the code another type of the list is fetched by passing a different key. For this implementation the keys are Country and Product. In a real application one can have more than this.

The preceding implementation follows the SOLID principle, just one exception is the Factory class that must be modifed when one wants to add a new key so it breaks the rule of single responsibility.

Note: Here the country and product list are hardcoded but in an actual program it is fetched from the database.

Conclusion

This pattern is very helpful in the scenario where one wants to create a pool of objects and share them among the clients (the clients are software programs or classes or web applications as in this example).

Note: This is my point of view regarding patterns. Please provide your feedback regarding it and also provide feedback if something you find is wrong in this.

No comments:

Post a Comment