How to use the Flyweight design pattern in C#

Take advantage of the Flyweight design pattern to reduce memory consumption when working with many similar objects

How to use the Flyweight design pattern in C#
thinkstock

Design patterns help us solve design problems often encountered in software development and reduce the complexities in our code. The Gang of Four design patterns fall into three categories: creational, structural, and behavioral.

The Flyweight design pattern falls in the structural category. The Flyweight pattern helps reduce memory consumption when working with many similar objects at the same time. This article examines how we can work with the Flyweight design pattern in C#.

To work with the code examples provided in this article, you should have Visual Studio 2019 installed in your system. If you don’t already have a copy, you can download Visual Studio 2019 here.

Create a console application project in Visual Studio

First off, let’s create a .NET Core Console Application project in Visual Studio. Assuming Visual Studio 2019 is installed in your system, follow the steps outlined below to create a new .NET Core Console Application project in Visual Studio.

  1. Launch the Visual Studio IDE.
  2. Click on “Create new project.”
  3. In the “Create new project” window, select “Console App (.NET Core)” from the list of templates displayed.
  4. Click Next.
  5. In the “Configure your new project” window, specify the name and location for the new project.
  6. Click Create.

You should now have a new .NET Core console application project ready to go in Visual Studio 2019. We’ll use this project in the subsequent sections of this article.

The Flyweight design pattern and its usage

The Flyweight design pattern reduces the amount of memory required to create a number of large objects of the identical type in an application. The flyweight is an object that reduces memory pressure by sharing data with similar objects. Thus a new object is created only if needed, i.e., if no matching object is available. By reducing the memory intake, the Flyweight pattern improves performance.

A typical use case for the Flyweight pattern is when you need to create large objects of the same type. The Gang of Four description: “Use sharing to support large numbers of fine-grained objects efficiently.”

A flyweight object can have either of the following states:

  • Intrinsic state — the data is state-independent and is typically stored inside the flyweight object and can be shared
  • Extrinsic state — the data is state-dependent and hence it cannot be shared

The participants in the Flyweight design pattern include the following:

  • Flyweight — this is typically an interface for the flyweight objects
  • ConcreteFlyweight — this is a class that implements the Flyweight interface
  • FlyweightFactory — this is a factory class used to create concrete objects of the ConcreteFlyweight type
  • Client — this stores references to the flyweight instances

Implement Flyweight abstract class and method in C#

To implement the Flyweight design pattern in C#, let’s start by defining the abstract base class named Flyweight. This class will contain the declaration of an abstract method named Display. The following code snippet illustrates the Flyweight abstract class.

public abstract class Flyweight
 {
     public abstract void Display();
 }

The ConcreteFlyweight class extends the Flyweight class and implements the abstract method. The following code listing shows the ConcreteFlyweight class.

public class ConcreteFlyweight : Flyweight
    {
        private readonly string _key = null;
        public ConcreteFlyweight(string key)
        {
            _key = key;
        }
        public override void Display()
        {
            Console.WriteLine("The Key is: "+ _key);
        }
    }

Implement the FlyweightFactory class in C#

The FlyweightFactory class creates and stores flyweight instances in an instance of Dictionary. This class contains the GetFlyweight() method that returns a ConcreteFlyweight instance based on a given key. If the instance is present in the Dictionary then it is returned, else a new instance of ConcreteFlyweight is created and returned. The following code listing illustrates the FlyweightFactory class.

public class FlyweightFactory
    {
        private Dictionary<string, Flyweight> flyweights = new
        Dictionary<string, Flyweight>();
        public Flyweight GetFlyweight(string key)
        {
            Flyweight flyweight = null;
            if (flyweights.ContainsKey(key))
            {
                flyweight = flyweights[key];
            }
            else
            {
                flyweight = new ConcreteFlyweight(key);
                flyweights.Add(key, flyweight);
            }
            return flyweight;
        }
    }

Note that in this example we are using one ConcreteFlyweight class. Multiple instances of this class differ from one another on the value of the key. In practice, you might want to create several concrete classes that extend the Flyweight class. We’ve omitted multiple instances here for simplicity.

Implement the Flyweight client in C#

Lastly, here is what the client would look like.

static void Main(string[] args)
{
  var factory = new FlyweightFactory();
  var flyweight = factory.GetFlyweight("A");
  flyweight.Display();
  Console.Read();
}

Flyweight design pattern example in C#

Here is the complete program for your reference.

public abstract class Flyweight
    {
        public abstract void Display();
    }
    public class ConcreteFlyweight : Flyweight
    {
        private readonly string _key = null;
        public ConcreteFlyweight(string key)
        {
            _key = key;
        }
        public override void Display()
        {
            Console.WriteLine("The Key is: "+ _key);
        }
    }
    public class FlyweightFactory
    {
        private Dictionary<string, Flyweight> flyweights = new
        Dictionary<string, Flyweight>();
        public Flyweight GetFlyweight(string key)
        {
            Flyweight flyweight = null;
            if (flyweights.ContainsKey(key))
            {
                flyweight = flyweights[key];
            }
            else
            {
                flyweight = new ConcreteFlyweight(key);
                flyweights.Add(key, flyweight);
            }
            return flyweight;
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            var factory = new FlyweightFactory();
            var flyweight = factory.GetFlyweight("A");
            flyweight.Display();
            Console.Read();
        }
    }

When you execute the above program, you should see the following output. 

flyweight design pattern c IDG

Figure 1: The Flyweight design pattern in action!

You can take advantage of the Flyweight design pattern in scenarios where availability of memory is a constraint. However, in practice, the complexities involved in implementing the Flyweight design pattern often outweigh the benefits. Hence, the usefulness of the Flyweight pattern is limited. In many cases, a better alternative is the Prototype design pattern.

Copyright © 2020 IDG Communications, Inc.