Share Common Parts of State between Multiple Objects
Swift â Problems Catalogue #14
Problem Definition:
Consider the following scenario. You have to implement an app that lets you control an army of flies, a FlyCommandApp đȘ°đź (thatâs an odd use caseâŠ).
The problem here is that there are a lot and I mean a lot of flies in a swarm. Ergo, potentially a lot of objects that of course need to be stored.
Even though in our day and age, we tend not to worry about memory consumption, sometimes, in some particular cases, we canât just create instances upon instances of objects (especially when we need vast amounts of them).
Problem Solution:
Solution âFlyweight itâs a structural design pattern that allows you to share common parts of state between multiple objects thus manages to minimize memory consumption. Basically, it returns cached objects instead of creating new ones.
Itâs not a widely encountered design patternâquite the opposite but itâs a good one to know when you are working in a significant memory (RAM) consumption context. Its sole purpose is to save memory by caching common data used by different objects.
Real-World Usage:
First, letâs define some enums in which we define the types of swarm formation and its stance. Next, letâs define a Unit protocol (in case we want to expand our Swarm to also support different types of insects đ) in which we define a move() method with a type of stance and a type of formation.
Afterwards, itâs time to define our Fly entity. This entity has a description and conforms to the Unit protocol.
Next, we need to create our SwarmControl class. This class takes an array of flies, their stance and their type of formation.
Also, letâs define an executeFlyManoeuvre() method that for each fly in our swarm executes the move method with a stance and in a particular formation.
So far, so good. Next, we need to handle the gist of the Flyweight pattern or where the magic of this pattern happens.
Here, two methods and the swarm variable are of Flyweight importance. The createFly(), deployFly() and the swarm dictionary with the FlyType key.
In essence, the deployFly() method, before returning any Fly, checks the swarm, if an entity of that particular requested FlyType exists, then it returns that entity. If not, then it creates one with the createFly() method, stores it in the swarm and afterwards returns it.
What does that mean? We only have two particular entities in the swarm variable â one leader and one fighter but we can deploy them several times.
Also, to illustrate this even further, letâs add a swarmDescription() method that prints out all the entities in the swarm. We will call this method after we deploy our swarm to illustrate the Flyweight concept.
Letâs bring everything together in the FlyCommandApp. Letâs define in the test method four flies and add them to a swarm. Afterwards, letâs pass the swarm into the swarmControl constructor, execute the Fly manoeuvre and afterwards letâs execute the swarm description from the factory.
Now, letâs see the output. First of all, we can see right off the bat, four movements, three deployments of the fighter type and one leader.
But afterwards, the simple âleaderâ & âfighterâ print from the swarmDescription() that represents the entities in the factory swarm:
1x leader and 1x fighter versus 4 deployments (3 figthers and 1 leader)
We need to store only two types of entities but we can deploy several hundred or more. Basically, the Flyweight pattern returns cached objects instead of creating new ones.
From this point on, the sky is the limit đ wellâŠalmost.
Of course, this design pattern has its limitations but used in moderation, itâs a great tool in our development toolbox.
This is the next article in the Swift Problems Catalogue series in which Iâll tackle general software development problems. The aim is to have a quick reference guide that can be easily accessed when having a design/algorithm dillemma.
Let me know what you think and donât be shy to share where and when this pattern simplified your coding experience đ¶