BlackWaspTM

This web site uses cookies. By using the site you accept the cookie policy.This message is for compliance with the UK ICO law.

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.

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
19 November 2008