Blog / Design Patterns
The Flyweigt Design Pattern in C# and .Net
  • Dec 26, 2020
  • 96
  • 97

The sixth design pattern in the Structural Pattern is the Flyweight Design Pattern. We will see what is this design pattern and how can we implement it in our program.

Note: Code can be downloaded at my Github.


Advertisement
 


1. What is the Flyweight Design Pattern?

The Flyweight Design Pattern helps to reduce memory usage and is used to create a large number of similar objects. 

Flyweight is a structural design pattern that lets you fit more objects into the available amount of RAM by sharing common parts of state between multiple objects instead of keeping all of the data in each object.

2. When to use the Flyweight Design Pattern?

The Flyweight Design Pattern is mostly used in Game applications which may create many similar objects at once (let's say 105 objects). One important feature of flyweight objects is that they are immutable. This means that they cannot be modified once they have been constructed.

Why do we care for number of objects in our program?

  • Less number of objects reduces the memory usage, and it manages to keep us away from errors related to memory.
  • Although creating an object is really fast, we can still reduce the execution time of our program by sharing objects.

3. How to implement the Flyweight Design Pattern?

The Flyweight Design Pattern is somehow complicated and is not popular to use. However, below is how we implement it in the code with a Car Factory application example.

Below is a Car class:

1
2
3
4
5
6
7
8
9
10
11
12
public class Car
{
    public string Owner { get; set; }
 
    public string Number { get; set; }
 
    public string Company { get; set; }
 
    public string Model { get; set; }
 
    public string Color { get; set; }
}

Step 1: Create a Flyweight class

The Flyweight stores a common portion of the state(also called intrinsic state) that belongs to multiple real business entities.The Flyweight accepts the rest of the state(extrinsic state, unique for each entity) via its method parameters.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Flyweight
{
    private Car _sharedState;
 
    public Flyweight(Car car)
    {
        this._sharedState = car;
    }
 
    public void Operation(Car uniqueState)
    {
        string s = JsonConvert.SerializeObject(this._sharedState);
        string u = JsonConvert.SerializeObject(uniqueState);
        Console.WriteLine($"Flyweight: Displaying shared {s} and unique {u} state.");
    }
}

Step 2: Create a Flyweight Factory

The Flyweight Factory creates and manages the Flyweight objects. It ensures that flyweights are shared correctly. When the client requests a flyweight, the factory either returns an existing instance or creates a new one, if it doesn't exist yet.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
public class FlyweightFactory
{
    private List<Tuple<Flyweight, string>> flyweights = new List<Tuple<Flyweight, string>>();
 
    public FlyweightFactory(params Car[] args)
    {
        foreach (var elem in args)
        {
            flyweights.Add(new Tuple<Flyweight, string>(new Flyweight(elem), this.getKey(elem)));
        }
    }
 
    // Returns a Flyweight's string hash for a given state.
    public string getKey(Car key)
    {
        List<string> elements = new List<string>();
 
        elements.Add(key.Model);
        elements.Add(key.Color);
        elements.Add(key.Company);
 
        if (key.Owner != null && key.Number != null)
        {
            elements.Add(key.Number);
            elements.Add(key.Owner);
        }
 
        elements.Sort();
 
        return string.Join("_", elements);
    }
 
    // Returns an existing Flyweight with a given state or creates a new
    // one.
    public Flyweight GetFlyweight(Car sharedState)
    {
        string key = this.getKey(sharedState);
 
        if (flyweights.Where(t => t.Item2 == key).Count() == 0)
        {
            Console.WriteLine("FlyweightFactory: Can't find a flyweight, creating new one.");
            this.flyweights.Add(new Tuple<Flyweight, string>(new Flyweight(sharedState), key));
        }
        else
        {
            Console.WriteLine("FlyweightFactory: Reusing existing flyweight.");
        }
        return this.flyweights.Where(t => t.Item2 == key).FirstOrDefault().Item1;
    }
 
    public void listFlyweights()
    {
        var count = flyweights.Count;
        Console.WriteLine($"\nFlyweightFactory: I have {count} flyweights:");
        foreach (var flyweight in flyweights)
        {
            Console.WriteLine(flyweight.Item2);
        }
    }
}

Done, we can now use the above code on the client side:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
class Program
{
    static void Main(string[] args)
    {
        // The client code usually creates a bunch of pre-populated
        // flyweights in the initialization stage of the application.
        var factory = new FlyweightFactory(
            new Car { Company = "Chevrolet", Model = "Camaro2018", Color = "pink" },
            new Car { Company = "Mercedes Benz", Model = "C300", Color = "black" },
            new Car { Company = "Mercedes Benz", Model = "C500", Color = "red" },
            new Car { Company = "BMW", Model = "M5", Color = "red" },
            new Car { Company = "BMW", Model = "X6", Color = "white" }
        );
        factory.listFlyweights();
 
        addCarToPoliceDatabase(factory, new Car
        {
            Number = "CL234IR",
            Owner = "James Doe",
            Company = "BMW",
            Model = "M5",
            Color = "red"
        });
 
        addCarToPoliceDatabase(factory, new Car
        {
            Number = "CL234IR",
            Owner = "James Doe",
            Company = "BMW",
            Model = "X1",
            Color = "red"
        });
 
        factory.listFlyweights();
    }
 
    public static void addCarToPoliceDatabase(FlyweightFactory factory, Car car)
    {
        Console.WriteLine("\nClient: Adding a car to database.");
 
        var flyweight = factory.GetFlyweight(new Car
        {
            Color = car.Color,
            Model = car.Model,
            Company = car.Company
        });
 
        // The client code either stores or calculates extrinsic state and
        // passes it to the flyweight's methods.
        flyweight.Operation(car);
    }
}

4. Conclusion

The Flyweight Design Pattern may be recategorized as the Creational Design Pattern that returns cached objects instead of creating new.

I hope this post is helpful and please let me know your thoughts in the comment section below. See you in the next post!


If you have a Website or a Web API developed by using .Net Core and looking for a way to publish your applications, this post will explain how to do it using GoDaddy Windows Hosting.Note: at this mome ...

Search text in Stored Procedure in SQL SELECT DISTINCT o.name AS Object_Name, o.type_desc FROM sys.sql_modules m INNER JOIN sys.objects o ON m.object_id = o ...

Using cherry-pick to select specific commits for your Pull Request.1. Create a new branch based on the target of the Pull Requestgit branch cherry-branch origin/master2. Switch to a new branchgit chec ...

After deployment Angular and API on IIS, it's working fine unless I refresh the page. Once refreshed, the web encountered 404 error. In this article, I will explain how to resolve this.Since Angular i ...

There are some benefits of keeping both UI and API parts in the same place for small projects. In this article, I will explain how I did to deploy Angular Web and ASP .Net Core API in the same folder ...

I got CORS error after publishing my API and Angular app to IIS even though CORS is enabled and the origins of the Angular app is added. Below is how I resolved this issue.Just simple, make sure you s ...

1. The Situation:Error Message:&nbsp;Pulse Secure Application failed to load Java. Please install correct JRE version.Description: This issue happens when I'm using a M1 Mac with a correct version of ...

Accelerated Mobile Pages (AMP)&nbsp;focuses on delivering static content from publishers as quickly as possible and possibly rewards early adopters with a boost in rank. Let's see how to implement it ...

Below is how to decrypt/convert a Hex string value into text using VB.Net:Decrypting Hex string value to string in VB.Net Function HexToString(ByVal hex As String) As String Dim text As New Sy ...