Flyweight pattern

The flyweight pattern enables the sharing of information in a resource-efficient way. You use it when you typically have the same information segment in a large number of objects and you decide to share this information between all of these large number of objects instead of having the same copy of information contained inside all of them.

For example, let's say in a software application, you have a list of a million employee records for a group of companies, and several thousands of employees belong to the same company and thus have similar information for their employer. Instead of having this information repeatedly copied into all of the employee objects, we share the common information into a separate object and let all those employees refer to the same object. In this way, we save a lot of memory space. Note that this is almost the way in which we design and relate entities in the relational database.

In most of the popular programming languages, there are some types that are immutable, for example, strings and other primitive types. This means that, if there is another object with the same value (the same characters) of the string, it will not have its own copy of strings; rather, they refer to the same string in memory as managed by the runtime. The string is immutable in .NET as well as in Java.

It is quite common to have flyweight objects to be immutable objects so that they automatically share the same memory space, such as the string value of the company name.

In general implementations, we have a flyweight factory that provides the flyweight objects that are then shared across. The client code uses this factory to get/add the flyweight objects in a way that this factory actually acts as memory storage or holder of the flyweight (shared) objects.

Let's look at the flyweight's general UML diagram:

We will now consider a practical example of the flyweight pattern. Let's say we have a flight reservation system that has a list of flights, passengers booked in them, and the reservation records.

Since the flight number and its meta information is shared across various reservations and passengers, we create the flyweight of the flight objects; all the flight objects are immutable objects (basically carrying only the string members) so that they become resource-efficient for sharing. Consider the following diagram:

Let's jump into the code and look at how classes actually look like in C# for .NET Core. We first look at the flyweight factory class, its design, and the sample code:

    public class FlightList 
{
/// <summary>
/// Thread-safe internal storage
/// </summary>
private readonly ConcurrentDictionary<string,
Flight> _cache = new ConcurrentDictionary<string, Flight>();

public FlightList()
{
PopulateFlightList(); //List of available flights
by the given carrier (airline)
}

/// <summary>
/// Returns immutable (and shareable flyweights) instances
/// </summary>
/// <param name="flightNumber"></param>
/// <returns></returns>
public Flight GetFlight(string flightNumber)
{
Flight flight = null;
if (_cache.TryGetValue(flightNumber, out flight))
return flight;
throw new FlightDoesNotExistException();
}

public void AddFlight(string flightNumber, string from,
string to, string planeType)
{
var flight = new Flight(flightNumber, from, to,
planeType);
_cache.AddOrUpdate(flightNumber, flight,
(key, oldFlight) => flight);
}

The factory object allows the adding and retrieving of the flight (flyweight) objects and holds them in a thread-safe dictionary. We have added a dummy code fragment to populate a few dummy flights just for demo purposes. You can see them in the actual code, but it's not important for the pattern.

Here is our Flight class, which is an immutable flyweight object:

    public class FlightList 
{
/// <summary>
/// Thread-safe internal storage
/// </summary>
private readonly ConcurrentDictionary<string, Flight> _cache =
new ConcurrentDictionary<string, Flight>();

public FlightList()
{
PopulateFlightList(); //List of available flights by
the given carrier (airline)
}

/// <summary>
/// Returns immutable (and shareable flyweights) instances
/// </summary>
/// <param name="flightNumber"></param>
/// <returns></returns>
public Flight GetFlight(string flightNumber)
{
Flight flight = null;
if (_cache.TryGetValue(flightNumber, out flight))
return flight;
throw new FlightDoesNotExistException();
}

public void AddFlight(string flightNumber,
string from, string to, string planeType)
{
var flight = new Flight(flightNumber, from, to,
planeType);
_cache.AddOrUpdate(flightNumber, flight,
(key, oldFlight) => flight);
}

The following FlightReservation class uses the FlightList factory class to get the flights for each reservation and binds them into a Reservation object:

    public class FlightReservation 
{
private readonly IList<Reservation> _reservations = new
List<Reservation>();
private FlightList _flightList;

public FlightReservation(FlightList flightList)
{
_flightList = flightList;
}

public void MakeReservation(string lastName, string id,
string flightNumber)
{
int pnr = PNRAllocator.AllocatePNR(lastName, id);

//Flyweight-Immutable object is returned which will be
shared between all instances
Flight flight = _flightList.GetFlight(flightNumber);

_reservations.Add(new Reservation(pnr, flight));
}

public void DisplayReservations()
{
//Print Total Reservations: _reservations.Count;
foreach (var reservation in _reservations)
reservation.Display();
}
}

Take a look at the tiny Reservation class that binds the reservation of a passenger created by FlightReservation:

    public class Reservation 
{
private readonly int _pnr;
private readonly Flight _flight;

public Reservation(int pnr, Flight flight)
{
_pnr = pnr;
_flight = flight;
}

public string Display()
{
//concat all properties and return as a single string
return "";
}
}

Finally, let's look at a simple .NET Core XUnit-based test case demonstrating sample usage. I do not think it requires further explanation, as the test code is fairly straight forward. Do not forget to download the whole source code from the Packt website:

    public class FlyweightTests 
{
private static IServiceProvider Provider { get; set; }

[Fact]
public void Test_Flyweight_Pattern()
{
RegisterServices();
FlyweightClient();
}

/// <summary>
/// Initializing & populating DI container
/// </summary>
private void RegisterServices()
{
IServiceCollection services = new ServiceCollection();

//Adding required dependencies to the DI Container
services.AddSingleton<FlightList>();
services.AddTransient<FlightReservation>();

Provider = services.BuildServiceProvider();
}

private void FlyweightClient()
{
var flightReservationSystem =
Provider.GetRequiredService<FlightReservation>();

flightReservationSystem.MakeReservation("Qureshi",
"NRJ445", "332");
flightReservationSystem.MakeReservation("Senthilvel",
"NRI339", "333");
flightReservationSystem.MakeReservation("Khan",
"KLM987", "333");

flightReservationSystem.DisplayReservations();
}
}
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.149.242.175