Table of contents
Foreword
The factory pattern is a creational pattern that usually makes use of conditional statements such as if
and switch
. Today, we will be showcasing the factory pattern in the context of just dealing with singleton (static) values.
Imagine a game world which has created a particular entity for us. Before it can actually bring that entity to the world, it would need to determine the spawn behavior of the entity. Likewise, players spawn in their houses, monsters spawn in dark areas, animals spawn in grassy plains and so on. The strategy pattern is another pattern that is also a perfect fit in defining these behaviors.
Consider having a SpawnStrategyFactory
. Given an entityname
, it creates and returns the proper SpawnStrategy
subclass. Here is that example of that using if
statements:
public getStrategyFromEntityName(name: string): SpawnStrategy {
if (name === 'player') {
return new SpawnPlayerStrategy();
}
else if (name === 'monster') {
return new SpawnMonsterStrategy();
}
else if (name === 'animal') {
return new SpawnAnimalStrategy();
}
return null;
}
Here is a functionally equivalent and cleaner way using switch
statements:
public getStrategyFromEntityName(name: string): SpawnStrategy {
switch(name) {
case 'player':
return new SpawnPlayerStrategy();
case 'monster':
return new SpawnMonsterStrategy();
case 'animal':
return new SpawnAnimalStrategy();
default:
return null;
}
}
Problem
What if you just wanted deal with singleton values? Like, you have no need to create a new instance for every call. Like in our example, the strategy classes just seemingly hold behavior and it does not even vary when created.
Evidently, and the more entity subclasses that you create, the more the conditional statement chain grows. Maintaining a chain of if
and switch
can become eventually unbearable and unnecessary burden for us.
Solution
A solution could be to utilize the Map data structure to make the factory pattern more elegant. This is a valid approach when you just are dealing with singleton values. You would store the name (or whatever key you prefer) and subclasses as key-value pairs. Let us revisit the earlier example, shall we?
class SpawnStrategyFactory {
protected strategies: Map<string, SpawnStrategy>;
constructor() {
this.strategies = new Map<string, SpawnStrategy>();
this.strategies.set('player', new SpawnPlayerStrategy());
this.strategies.set('monster', new SpawnMonsterStrategy());
this.strategies.set('animal', new SpawnAnimalStrategy());
}
}
Assuming SpawnStrategyFactory
is a singleton, the strategies are defined and instantiated on the onset. With this, the strategy classes are readily made available and your getter function would look like:
public getStrategyFromEntityName(name: string): SpawnStrategy {
return this.strategies.get(name);
}
Closing Thoughts
If you are familiar with data structures, it is said that you retrieve the elements from a map in O(1) time complexity; which is near instantaneous. Consequently, the trade-off would be memory; resulting in a space complexity of O(n). The more you create subclasses, the more key-value pairs that you would have to store in a map.