Get up to 80 % extra points for free! More info:

Flyweight

The Flyweight design pattern saves memory for tasks for which we need to create a large number of instances. We can't always use this pattern - created instances must meet certain criteria that we're gonna discuss further. We'll also take a look at another implementation that's not officially considered as a flyweight but solves the same problem - just with another method.

Principle

The basic idea behind this design pattern is to divide the class into two parts. The first part is common to all classes, and we'll call it the intrinsic state of the object. The instance remembers this state itself. For example, let's consider we're programming a game and have enemies in the world. Each enemy is totally identical - they look the same, have the same strength, intelligence, weapons, and so on. There's no reason to remember all the information for each object separately (and waste memory). Instead, we'll move this information into a separate class that will represent our intrinsic state. However, how does the enemy find out where they're on the map? Someone has to tell them from the outside. The called method gets this information directly in its parameters. We call them the extrinsic state of an object which the object doesn't remember and must retrieve it.

Text rendering

We'll show the implementation on another example and get back to games at the end of the article. Traditionally, this pattern is explained on text manipulation examples, so we'll use it here as well. The intrinsic state will be represented by information such as font, font decoration (italics, underline), font size, formatting, and so on. This information is generally common to a large part of the text - for the whole paragraph, for all titles and so on. To keep things simple, a character being typed will be also in the intrinsic representation.

As a result, we have all the letters generated for each style we use in the text. Mathematically, it means the number of styles multiplied by the number of letters (depending on what character set we take into account). It seems that the number of instances has "swollen" in total, but it's still less than if we had a separate object for each character in the text. Now, the object only needs to know where to be rendered - we'll give it to it from the outside.

Implementation

The flyweight also requires using other design patterns. Objects of the intrinsic state differ from each other only by attributes. So there's no reason to have two instances with the same attributes in the program. A factory will help us with this problem. It'll store already created instances and if the user requires another instance with the same attributes, it'll return the already created instance. The text itself will be stored as a sequence of intrinsic states. The rendering will be done by going through the sequence, and the characters will be rendered one after another. Now to the practical part.

The UML diagram of the Flyweight GOF design pattern sample - GOF - Structural Patterns

The Character class is the intrinsic representation of the state. Characters are created through CharacterFactory, which keeps the created instances in its private attribute. Since the Character class is an immutable object, we only need to search by the hash. The text then stores a sequence of characters and renders them one by one. For our case, we omit the positioning of the whole text. The concrete implementation would then be the following:

class Character
{
    public char Character;
    public string FontName;
    public int Size;

    public int GetHashCode()
    {
        return Hash(this.Character, this.FontName, this.Size);
    }

    public void Draw(Rectangle where)
    {
        // rendered in the given place
    }

    public static int Hash(char Character, string FontName, int Size)
    {
        Znak.GetHashCode() * 31 + FontName.GetHashCode() * 17 + Size.GetHashCode();
    }
}



class CharacterFactory
{
    private Dictionary<int,Character> CreatedInstances;

    public Character GetCharacter(char Character, string FontName, int Size)
    {
        int AssumedHash = Character.Hash(Character, FontName, Size);
        Character Result;
        if (this.Instance.TryGetValue(AssumedHash, Result))
            return Result;
        Result = new Character();
        Result.Character = Character;
        Result.FontName = FontName;
        Result.Size = Size;
        this.Instance.Add(Result.GetHashCode(), Result);
        return Result;
    }
}

We create a static Hash() method in the Character class. This method allows us to simulate the GetHashCode() method without instantiating itself. We then use it in the factory. The factory looks whether it already has an instance of the Character class with the specified values. If it doesn't, it creates it, stores it, and returns it to the user. The rendering itself might look as follows (a simplified version):

class Text
{
    public List<Character> Characters;

    public void Draw()
    {
    Rectangle RectangleToDraw;
        foreach (Character c in this.Characters)
        {
            c.Draw(RectangleToDraw);
            RectangleToDraw.MoveByCharWidth();
        }
    }
}

The extrinsic state (the position to render) is passed as a parameter when rendering. For the user, it only means to fill the list with characters, and the Text renders its characters itself. If some character repeats, there will be only a single instance of it in the program (but inserted in multiple places). This is the Flyweight principle.

Possible modifications

Finally, I'd like to discuss a similar design that isn't officially listed as a Flyweight but solves the same problem. Let's get back to the promised group of enemies. The enemies are moving on the map - so we need to know their coordinates. The problem is, in this case, that we can't apply the Flyweight pattern well enough. The coordinates change and we don't have a reliable structure to reflect these changes. The solution would be to declare a separate class for the intrinsic state (as we have known it up to now) and another class for the extrinsic state. The class for the extrinsic state would have a private attribute of the intrinsic state. The example should explain the situation:

class BasicEnemy
{
    public Texture Appearance;
    public int Power;
    public int MaxLives;
    public int Intelligence;
}

class Enemy
{
    private BasicEnemy Basis;
    public int Lives;
    public int XPosition;
    public int YPosition;

    public Enemy(BasicEnemy BasisParameter)
    {
        this.Basis = BasisParameter;
    }
}

Unlike with the Flyweight pattern, we won't reduce the number of instances (we must actually create as many instances as there are objects in the game), but we'll save memory. The common part has at least 4 * 3 = 12 bytes for the int type and 8 bytes for the texture (we'll assume it's a pointer). If we had 1024 enemies, we'd save at least 20 * 1024 = 20kB, and that's not a negligible amount. It also gives us more flexibility. For example, we can include the number of lives into the intrinsic state (most of the time all the monsters will have the full number of lives) and we can replace the intrinsic state when they are wounded. We can also nest the intrinsic states (an intrinsic state which has an intrinsic state).

Design patterns are not laws. They're more like pages from a cookbook that we can customize to suit our needs. There's always a case when it's necessary to think of a specific system design. Design patterns give us an insight into how this could be solved, but they certainly don't say how it must be solved. That's why the programmers are here.


 

All articles in this section
GOF - Structural Patterns
Article has been written for you by David Capka Hartinger
Avatar
User rating:
No one has rated this quite yet, be the first one!
The author is a programmer, who likes web technologies and being the lead/chief article writer at ICT.social. He shares his knowledge with the community and is always looking to improve. He believes that anyone can do what they set their mind to.
Unicorn university David learned IT at the Unicorn University - a prestigious college providing education on IT and economics.
Activities