We all know that a factory creates an object or product and sells it to the consumer. Let’s take an example of a Video game. We all play on PS4 but most consumers are not aware of how PS4 was made. They just buy the product and start using it.
Factory Design pattern is the same idea. It is a part of the Creational Design Pattern.
The basic concept of the Factory Pattern is to create an object that, in turn, create other objects based on conditions.
When Do We Need a Factory Pattern?
We need to choose Factory Pattern under the following circumstances:
- The Classes don’t know what exact sub-classes they have to create
- The Object needs to be extended to subclasses
- The Product implementation tends to change over time and the Client remains unchanged
Let’s take an example,
In this example, we are creating a function which will return an instance of a class based on a certain condition.
- let Factory = () => {
- let data;
- this.getType = (type) => {
- if (type === 'one') {
- data = require('./class-one')();
- }
- if (type === 'two') {
- data = require('./class-two')();
- }
- return data;
- }
- }
- Factory.getType('two');
Factory Pattern allows the following,
- To separate the object creation from its implementation
- To create different instances based on conditions
- Not to expose the constructor of the class/objects.
Now, let’s take a real-world implementation to understand about Factory Pattern.
UML diagram for the example
As discussed earlier, the Client (the gamer) will make a deal with the store (User Factory) to use the service. The Factory will create the PS4 (internal implementation) which will be hidden from the outside world.
person.js
- class Person {
- constructor(name){
- this.name = name;
- }
- }
-
- module.exports = Person;
We have created a parent class Person which contains the common property (ex: name) for student and instructor. We can also add another class, like employee, sportsman, etc.
student.js
- const Person = require('./person');
- class Student extends Person {
- constructor(name, level) {
- super(name);
- this.level = level;
- }
- toString() {
- return JSON.stringify(this);
- }
- }
- module.exports = Student;
instructor.js
- const Person = require('./person');
- class Instructor extends Person {
- constructor(name, position, earnings) {
- super(name);
- this.position = position;
- this.earnings = earnings;
- }
- toString() {
- return JSON.stringify(this);
- }
- }
- module.exports = Instructor;
We have created two child classes which were inherited from parent class (person.js), with their own certain properties. The toString() method will print the current instance of the specified class.
userFactory.js
- const Instructor = require('./instructor');
- const Student = require('./student');
- const userFactory = (type, name, position, earnings = 0, level = 'Beginner') => {
- if (type === 'instructor') {
- return new Instructor(type, name, position, earnings);
- }
- if (type === 'student') {
- return new Student(type, name, level);
- }
- }
- module.exports = userFactory;
In this file, we are creating a factory which will create the instance based on client requirements. As we know creating instance will take up some memory space, hence it’s a good practice to create an instance which we need for a specified purpose.
main.js
- const userFactory = require('./userFactory');
- const paul = userFactory('instructor', 'Paul', 'software engineer', 1000);
- const john = userFactory('student', 'John', 'Beginner');
- console.log(paul.toString());
- console.log(john.toString());
From the main.js file we are calling the factory method ‘userFactory’ and passing our requirements. Hence we are getting instance of our specific needs.
In this blog, you have learned about the factory pattern design principle in JS. Hope you have liked it. In my next article, you will come across the other design patterns, such as Singleton, Builder Pattern, etc. in JavaScript.