JavaScript Factory Pattern [In-Depth Tutorial]


Olorunfemi Akinlua

Linux

What is the JavaScript Factory Pattern?

The Factory Pattern is a design pattern that allows developers to create objects without having to specify their exact class. In other words, it provides an interface for creating objects in a super class, but leaves the actual creation of those objects to the sub-classes. This pattern is useful when you need to create multiple objects that share a common interface, but have different implementations.

 

How does the Factory pattern work in JavaScript

In JavaScript, the Factory pattern involves creating a function that returns a new object. This function is called the Factory function, and it encapsulates the object creation process. The Factory function can accept parameters that are used to determine the type and configuration of the objects being created. The objects can be created using a constructor function, or they can be created using plain object literals.

 

Different types of Factory patterns in JavaScript

There are several different types of Factory patterns in JavaScript, including:

  • Simple Factory: A factory function that creates objects without using a constructor function.
  • Factory Method: A factory method that is implemented in a superclass and allows subclasses to override the creation of objects.
  • Abstract Factory: A factory that creates families of related objects without specifying their concrete classes.
  • Singleton Factory: A factory that creates only a single instance of an object and returns it on subsequent calls to the factory function.

 

Implementing Factory Patterns in JavaScript

In JavaScript, the Factory Pattern can be implemented using different techniques. To showcase and factory patterns, we will develop and explain two detailed examples.

For the first example, we will use a function to create different types of objects based on a parameter that is passed to it.

function createObject(type) {
    let obj;
    if (type === "go") {
        obj = {
            prop1: "value1",
            prop2: "value2",
            goMethOne: function () {
                console.log("This is go method 1");
            },
            goMethTwo: function () {
                console.log("This is go method 2");
            },
        };
    } else if (type === "linux") {
        obj = {
            prop1: "value1",
            prop2: "value2",
            linuxMethOne: function () {
                console.log("This is linux method 1");
            },
            linuxMethTwo: function () {
                console.log("This is linux method 3");
            },
        };
    }
    return obj;
}

let objA = createObject("go");
let objB = createObject("linux");

console.log(objA);

Output

{
  prop1: 'value1',
  prop2: 'value2',
  goMethOne: [Function: goMethOne],
  goMethTwo: [Function: goMethTwo]
}

In this example, we define a function called createObject that takes a parameter called type. Based on the value of type, the function creates an object that has a common interface, but different implementations. The function returns the created object, which can be used by the client code.

We can work with another example to show the factory pattern. In this example, we will use a constructor function to create different types of objects.

function Go() {
    this.prop1 = "value1";
    this.prop2 = "value2";
}

Go.prototype.method1 = function () {
    console.log("This is Go method 1");
};

Go.prototype.method2 = function () {
    console.log("This is Go method 2");
};

function Linux() {
    this.prop1 = "value1";
    this.prop2 = "value2";
}

Linux.prototype.method1 = function () {
    console.log("This is Linux method 1");
};

Linux.prototype.method2 = function () {
    console.log("This is Linux method 2");
};

function createObject(type) {
    let obj;
    if (type === "Go") {
        obj = new Go();
    } else if (type === "Linux") {
        obj = new Linux();
    }
    return obj;
}

let go = createObject("Go");
let linux = createObject("Linux");

console.log(linux);

Output

Linux { prop1: 'value1', prop2: 'value2' }

In this example, we define two constructor functions (Go and Linux) that have a common interface, but different implementations. We then define a function called createObject that takes a parameter called type and returns an object of the appropriate type. The client code can then use the created objects to call their methods.

 

Can the Factory pattern be used with JavaScript classes

Yes, the Factory pattern can be used with JavaScript classes. You can create a Factory class that has methods for creating objects of different types. These methods can use the new keyword to create objects with the specified constructor functions. Here's an example:

class CarFactory {
  createSUV() {
    return new SUV("Toyota", "RAV4", "2021", "white", ["4WD", "backup camera", "Bluetooth"]);
  }

  createSedan() {
    return new Sedan("Honda", "Civic", "2022", "blue", ["Apple CarPlay", "blind spot monitoring", "sunroof"]);
  }
}

var factory = new CarFactory();
var mySUV = factory.createSUV();
console.log(mySUV);

In this example, the CarFactory class has two methods for creating SUV and sedan objects. These methods use the new keyword to create new objects of the SUV and Sedan classes, which are assumed to exist.

 

Can the Factory pattern be used with asynchronous code

Yes, the Factory pattern can be used with asynchronous code. You can create an asynchronous Factory function that returns a Promise that resolves to the new object. The object creation logic can use asynchronous functions or Promises to fetch data or perform other tasks before creating the object. Here's an example:

function createCarAsync(type) {
  return new Promise(function(resolve, reject) {
    // Use setTimeout to simulate an asynchronous delay of 1 second
    setTimeout(function() {
      var car;

      // Create a new car object based on the type argument
      if (type === "SUV") {
        car = {
          make: "Toyota",
          model: "RAV4",
          year: "2021",
          color: "white",
          features: ["4WD", "backup camera", "Bluetooth"]
        };
      } else if (type === "sedan") {
        car = {
          make: "Honda",
          model: "Civic",
          year: "2022",
          color: "blue",
          features: ["Apple CarPlay", "blind spot monitoring", "sunroof"]
        };
      }

      // Resolve the Promise with the new car object, or reject it with an error
      if (car) {
        resolve(car);
      } else {
        reject(new Error("Invalid car type"));
      }
    }, 1000);
  });
}

// Call the createCarAsync function with the "SUV" type argument
createCarAsync("SUV").then(function(car) {
  console.log(car);
}).catch(function(error) {
  console.error(error);
});

The createCarAsync function is defined as a function that takes a type argument, and returns a Promise. The Promise constructor takes two arguments: a resolve function and a reject function. These functions are called when the Promise is either fulfilled (resolved) or rejected. In this case, we use the setTimeout function to simulate an asynchronous delay of 1 second, and then create a new car object based on the type argument. If the type is not "SUV" or "sedan", we reject the Promise with an error message.

If the type is "SUV" or "sedan", we create a new car object with some properties, and then call the resolve function with the new object. This fulfills the Promise with the new object as its value.

Finally, we call the createCarAsync function with the "SUV" type argument, and use the then method to handle the fulfilled Promise. The then method takes a callback function that is called when the Promise is fulfilled. In this case, the callback function logs the new car object to the console.

If the Promise is rejected, we use the catch method to handle the rejection. The catch method takes a callback function that is called when the Promise is rejected. In this case, the callback function logs the error message to the console.

 

How is Factory pattern different from Abstract Factory pattern

The Factory pattern is a creational design pattern that provides an interface for creating objects in a superclass, but allows subclasses to alter the type of objects that will be created. The Abstract Factory pattern is also a creational pattern, but it creates families of related objects without specifying their concrete classes.

The Factory pattern is simpler and more flexible, while the Abstract Factory pattern is more complex and more focused on creating families of objects.

 

Advantages and Disadvantages of the JavaScript Factory Pattern

One advantage of the Factory Pattern is that it can improve code organization and scalability. By centralizing the object creation logic, the pattern reduces code duplication and makes it easier to modify and maintain the code. Additionally, the pattern can improve performance by minimizing the number of objects created and reducing memory usage.

However, there are also potential drawbacks to using the Factory Pattern. For example, the pattern can increase the complexity of the code and make it more difficult to understand. Additionally, the pattern can reduce the flexibility of the code by limiting the types of objects that can be created.

 

Summary

The Factory Pattern is a useful design pattern that allows developers to create objects without having to specify their exact class. In JavaScript, this pattern can be implemented using different techniques such as using a function or constructor function to create objects with a common interface but different implementations.

By using the Factory Pattern, developers can simplify their code, avoid duplicating code, and provide a flexible interface for creating objects. This pattern can be especially useful when dealing with a large number of objects that have similar characteristics but different behaviors.

 

References

Factory method pattern - Wikipedia
The Factory Pattern - Learning JavaScript Design Patterns [Book] (oreilly.com)

 

Views: 8

Olorunfemi Akinlua

He is boasting over five years of experience in JavaScript, specializing in technical content writing and UX design. With a keen focus on programming languages, he crafts compelling content and designs user-friendly interfaces to enhance digital experiences across various domains. You can connect with him on LinkedIn.

Can't find what you're searching for? Let us assist you.

Enter your query below, and we'll provide instant results tailored to your needs.

If my articles on GoLinuxCloud has helped you, kindly consider buying me a coffee as a token of appreciation.

Buy GoLinuxCloud a Coffee

For any other feedbacks or questions you can send mail to admin@golinuxcloud.com

Thank You for your support!!

Leave a Comment

GoLinuxCloud Logo


We try to offer easy-to-follow guides and tips on various topics such as Linux, Cloud Computing, Programming Languages, Ethical Hacking and much more.

Programming Languages

JavaScript

Python

Golang

Node.js

Java

Laravel