Organizing code is going to save us from a lot of pain. Using the features of Object Oriented programming, we can employ certain design patterns to achieve better readability, reduce redundancy and create abstractions, if needed. One such pattern is the factory pattern.
The factory pattern is a type of Object Oriented pattern which follows the DRY methodology. As the name suggests, object instances are created by using a factory to make the required object for us.
Educative.io: Learn by Coding ⤵
Check out “Ace the JavaScript Interview” - an interactive track
ⓘ About this sponsored link
Let’s have a look at a very simple example of using the factory pattern to assemble an alligator
object. To do that we first need to make factories that create the alligator
parts for us:
class TailFactory { constructor(props) { this.tailLength = props.tailLength; } }; class TorsoFactory { constructor(props) { this.color = props.color; } }; class HeadFactory { constructor(props) { this.snoutLenth = props.snoutLenth; } };
Now, we create a class that acts as an intermediary between the actual factories classes and the user. Let’s call this the ReptilePartFactory
:
class ReptilePartFactory { constructor(type, props) { if(type === "tail") return new TailFactory(props); if(type === "torso") return new TorsoFactory(props); if(type === "head") return new HeadFactory(props); } };
Let’s go ahead and assemble the actual alligator now and use the ReptilePartFactory
to get the required parts for us:
let alligator = {}; let alligatorProps = { tailLength : 2.5, color: "green", snoutLenth: 1 }; //gets a tail from the tail factory alligator.tail = new ReptilePartFactory("tail", alligatorProps); //gets a torso from the torso factory alligator.torso = new ReptilePartFactory("torso", alligatorProps); //gets a head from the head factory alligator.head = new ReptilePartFactory("head", alligatorProps);
Take at the look the pattern above, it seems like we could use the same ReptilePartFactory
to create parts for alligator-like objects. The factories in the background would never have to know about the nature of the final object.
Thus, using the factory pattern gives us certain advantages:
- Dynamic object creation: It can be used in cases where the type of the object is decided at runtime.
- Abstraction: The user never really has to access the actual object’s constructor.
- Reusability/Maintenance: Same factories can be used for similar objects and it allows us to add/remove new object classes easily without changing a lot of code.
Now that we have some understanding of the factory pattern, let’s explore a little bit on writing better factory pattern code.
The example above uses an if-ladder to find out which factory to call based on user input. This is a simple implementation, intuitive and not very open to changes. If we have new parts to add later, then we’d have to disturb the ReptilePartFactory
. This is a violation of SOLID principles, which states “Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification”.
How about we store the factory classes in an object and call the required part factory by using the part we want as the key? First we’d have to register the factories, it’d be as simple as:
let registeredPartFactories = {}; registeredPartFactories['tail'] = class TailFactory{ ... }; registeredPartFactories['torso'] = class TorsoFactory { ... }; registeredPartFactories['head'] = class HeadFactory { ... };
And now, the abstract layer can call the factories like this:
class ReptilePartFactory { constructor(type, props) { return new registeredPartFactories[type](props); } };
This approach is much cleaner and allows to expand our factories without affecting code in the ReptilePartFactory
.
In conclusion
There are several other object oriented patterns which also increase readability and quality. So before using the factory pattern, check if there is a real requirement for it. If you’re going to create similar types of objects repeatedly and also need a layer to create new instances using these objects while providing some level of abstraction to the creation logic, then yes - the factory pattern is a good option.