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 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.