BlackWasp
Design Patterns
.NET 1.1+

Flyweight Design Pattern

The flyweight pattern is a design pattern that is used to minimise resource usage when working with very large numbers of objects. When creating many thousands of identical objects, stateless flyweights can lower the memory used to a manageable level.

What is the Flyweight Pattern?

The flyweight pattern is a Gang of Four design pattern. This is a structural pattern as it defines a manner for creating relationships between classes. The flyweight design pattern is used to reduce the memory and resource usage for complex models containing many hundreds, thousands or even hundreds of thousands of similar objects.

When you need to create a very large number of objects, each requires an amount of memory to store the objects' state. Even if the storage requirements for each individual object are small, the number of objects may cause the overall memory usage to be high. Depending upon the scenario and the target environment, the memory usage may be so high that the program cannot execute.

In some cases, the objects being created may include information that is often duplicated. Where this is true, the flyweight pattern can be used. When this pattern is applied, the properties of the objects that are shared and are reasonably unchanging are moved into flyweight objects. For each of the main objects that use the shared data, only a reference to the appropriate flyweight object is required. This can drastically reduce the memory used by each of the main objects.

The flyweight pattern uses the concepts of intrinsic and extrinsic data. The intrinsic data is held in the properties of the flyweight objects that are shared. This information is stateless and generally remains unchanged, as any changes would be effectively replicated amongst all of the objects that reference the flyweight. Extrinsic data can be stateful as it is held outside of a flyweight object. It can be passed to methods of a flyweight when needed but should never be stored within a shared flyweight object.

The flyweight design pattern often uses a variation on the factory method pattern for the generation of the shared objects. The factory receives a request for a flyweight instance. If a matching object is already in use, that particular object is returned. If not, a new flyweight is generated. Usually the full set of available flyweight objects is held within the factory in a collection that can be accessed quickly, such as a Hashtable.

An appropriate use of the flyweight pattern is for a simulation that contains many similar items. For example, a war strategy simulation game may include the ability to monitor every individual unit on the battlefield. For a large battle, this could easily involve tens of thousands of infantry and vehicles. Holding the details of each object individually would use prohibitively large amounts of memory. However, as most units would share a set of stateless information, this could be extracted from the main objects to be held in flyweight objects.

Implementing the Flyweight Pattern

Flyweight Design Pattern UML

The UML class diagram above describes an implementation of the flyweight design pattern. The items in the diagram are described below:

  • FlyweightBase. This abstract class defines the members of the flyweight objects. This can be replaced with a simple interface if no base functionality is to be defined for inheritance by the flyweight classes that subclass it.
  • ConcreteFlyweight. Inheriting from the FlyweightBase class, the ConcreteFlyweight types provide the functionality and stateless information that will be shared amongst other objects.
  • UnsharedFlyweight. Although the flyweight design pattern enables sharing of information, it is possible to create instances of concrete flyweight classes that are not shared. In these cases, the objects may be stateful.
  • FlyweightFactory. This class holds references to each of the flyweight objects that have already been created. When the GetFlyweight method is called from client code, these references are checked to determine if an appropriate flyweight is already present. If one is, it is returned. If not, a new object is generated, added to the collection and returned.

The following shows the basic code of the flyweight design pattern implemented using C#. For compatibility with .NET 1.1, the flyweight collection is held in a simple Hashtable. In later versions of the .NET framework, you could use a generic dictionary for the same purpose.

public abstract class FlyweightBase
{
    public abstract void StatefulOperation(object o);
}


public class FlyweightFactory
{
    private Hashtable _flyweights = new Hashtable();

    public FlyweightBase GetFlyweight(string key)
    {
        if (_flyweights.Contains(key))
        {
            return _flyweights[key] as FlyweightBase;
        }
        else
        {
            ConcreteFlyweight newFlyweight = new ConcreteFlyweight();

            // Set properties of new flyweight here.

            _flyweights.Add(key, newFlyweight);
            return newFlyweight;
        }
    }
}


public class ConcreteFlyweight : FlyweightBase
{
    public override void StatefulOperation(object o)
    {
        Console.WriteLine(o);
    }
}


public class UnsharedFlyweight : FlyweightBase
{
    private object _state;

    public override void StatefulOperation(object o)
    {
        _state = o;
        Console.WriteLine(o);
    }
}

Example Flyweight

In this section, we will create an example of the flyweight pattern. This example will control the available units in a basic war simulation game. For simplicity, there are only two forms of unit. The Soldier class is the first unit type and represents a soldier on the battlefield with various properties that are required for the game. The property values have two possible sets of values depending upon whether the soldier is an infantryman or a marine. The second unit type is defined by the Tank class.

The following code shows the elements that are required to create the flyweight classes. For brevity, the properties are defined using C# 3.0 automatically implemented property syntax and the flyweights are held in a generic dictionary. No unshared flyweight creation is shown in this example, again to keep the code as short as possible.

Note the use of the FireAt method, which uses stateful information that is passed to the flyweight. This information determines the unit that will be attacked. Note also that within the factory method, the type of unit is tested and the correct class for instantiation is selected and populated with the values that will be shared through the flyweight object.

The final class in the sample code is the Target class. This defines objects that may be attacked. This class is also the consumer of the flyweight Unit class. Each Target object has a unique identifier and a reference to a Unit object containing the shared flyweight data.

public abstract class Unit
{
    public string Name { get; internal set; }
    public int Armour { get; internal set; }
    public int Speed { get; internal set; }
    public int RotationRate { get; internal set; }
    public int FireRate { get; internal set; }
    public int Range { get; internal set; }
    public int FirePower { get; internal set; }

    public abstract void FireAt(Target target);
}


public class UnitFactory
{
    private Dictionary<string, Unit> _units = new Dictionary<string, Unit>();


    public Unit GetUnit(string type)
    {
        if (_units.ContainsKey(type))
        {
            return _units[type];
        }
        else
        {
            Unit unit;

            switch (type)
            {
                case "Infantry":
                    unit = new Soldier();
                    unit.Name = "Standard Infantry";
                    unit.Armour = 5;
                    unit.Speed = 4;
                    unit.RotationRate = 180;
                    unit.FireRate = 5;
                    unit.Range = 100;
                    unit.FirePower = 5;
                    break;

                case "Marine":
                    unit = new Soldier();
                    unit.Name = "Marine";
                    unit.Armour = 25;
                    unit.Speed = 4;
                    unit.RotationRate = 180;
                    unit.FireRate = 3;
                    unit.Range = 200;
                    unit.FirePower = 10;
                    break;

                case "Tank":
                    unit = new Tank();
                    unit.Name = "Tank";
                    unit.Armour = 1000;
                    unit.Speed = 25;
                    unit.RotationRate = 5;
                    unit.FireRate = 30;
                    unit.Range = 1000;
                    unit.FirePower = 250;
                    break;

                default:
                    throw new ArgumentException();
            }

            _units.Add(type, unit);
            return unit;
        }
    }
}


public class Soldier : Unit
{
    public override void FireAt(Target target)
    {
        Console.WriteLine("Shooting at unit {0} with power of {1}."
            , target.ID, FirePower);
    }
}


public class Tank : Unit
{
    public override void FireAt(Target target)
    {
        Console.WriteLine("Firing at {0} with power of {1}.", target.ID, FirePower);
    }
}


public class Target
{
    public Unit UnitData;
    public Guid ID;
}

Testing the Flyweight

We can test the example flyweight classes using a console application. In the following Main method, the flyweight factory is instantiated and used to obtain the unit information that is referenced by two Target objects, both tanks. On the first occasion, the tank's unit data has not been created so is generated by the factory, added to its internal collection and returned. On the second occasion, the tank data already exists so the reference to the previously used instance is returned. This leads to both tanks requiring only a single, shared set of flyweight information.

The assignment of the result variable shows that both Target objects are indeed sharing the same Unit reference. The following line of code shows how the FirePower property can be obtained through the simple object model.

UnitFactory factory = new UnitFactory();

Target tank1 = new Target();
tank1.ID = Guid.NewGuid();
tank1.UnitData = factory.GetUnit("Tank");

Target tank2 = new Target();
tank2.ID = Guid.NewGuid();
tank2.UnitData = factory.GetUnit("Tank");

bool result = tank1.UnitData == tank2.UnitData;     // result = true
int firepower = tank1.UnitData.FirePower;           // firepower = 250
Link to this Page19 November 2008
TwitterTwitter RSS Feed RSS