How JavaScript works: the factory design pattern + 4 use cases

Lawrence Eagles
SessionStack Blog
Published in
9 min readNov 25, 2021

This is post # 53 of the series, dedicated to exploring JavaScript and its building components. In the process of identifying and describing the core elements, we also share some rules of thumb we use when building SessionStack, a JavaScript tool for developers to identify, visualize, and reproduce web app bugs through pixel-perfect session replay.

Introduction

Design patterns are templates or solution schemes for solving common software development problems.

Design patterns are divided into three categories: creational patterns, structural patterns, and behavioral patterns.

Creational design patterns consist of a group of design patterns that are concerned with object creation.

Creational patterns provide object creation mechanisms that promote flexibility and reusability of our code especially in situations where we get to create many different objects.

The factory pattern is a creational design pattern that provides a generic interface for creating objects. In the factory pattern, we can specify the type of object being created and we do not need to explicitly require a constructor.

In this article, we would take a deep dive into the factory design pattern in JavaScript.

Let’s start learning about it in the next section.

The Factory pattern in dept

The factory pattern is an object-oriented — — OOP design pattern that involves creating objects by using a factory.

A factory is an object or class or a function in a functional programming paradigm for creating objects.

Although there are different object creation methods in JavaScript, the factory pattern keeps our object creation logic concise and reusable.

Consider the code below:

In the code above, we created two Vehicle objects on the fly, by using the object literal syntax. The downside of this approach is the redundant methods: startEngine and drive.

Although in our small contrived example above these methods just log a string to the console, in reality, they could be complex. And repeating these methods the same way can lead to bugs. Also if there is a bug in the implementation of these methods we would have to fix it across all the objects they have been used.

We can clean things up by using factories. Factory gives us an object creation mechanism that is both flexible and reusable.

Consider the code below:

From our code above, we see that our factory function centralizes our object creation logic thereby, keeping our code DRY. Also, in case of any bug, we only need to fix it in the factory and it would reflect in all our code. Thus increasing our code maintainability.

Our example above is very basic and one can easily implement it using a constructor function or an ES6 class.

However, factories keep our code clean by centralizing our object creation logic and eliminating repeated constructor calls with the new operator.

The factory pattern shines if the object creation process involves dynamic factors or application configuration.

Imagine a vehicle factory that produces different kinds of vehicles: trucks, cars, buses ambulances, etc. We can implement this using the factory pattern as seen below:

In the code above, our Factory class creates a new vehicle by instantiating the Car class or the Truck vehicle class using the new operator. We determine which class to instantiate using the vehicleType parameter. In cases like these where the class to instantiate depends on dynamic factors the factory pattern is particularly useful.

Also, our factory promotes code reusability by reusing the StartEngine, driveVehicle, and stopEngine methods. So regardless of which type of vehicle is created by the factory, we can invoke these methods from the vehicle instance.

By using the factory pattern our code is also easily maintainable, so if the business expands and the factory starts producing new kinds of vehicles, we can easily refractory our implementation to handle this as seen below:

In the code above, we have extended our factory to support the production of buses and motorcycles. And all we have to do is to conditionally instantiate the Bus or Motorcycle class depending on the vehicleType.

Our Factory class implementation above relies heavily on class fields, a new JavaScript proposal. If you are not familiar with class fields, you can learn about them by reading our previous article that extensively covers class fields.

An alternative implementation is to make use of class constructors, this would allow us to create instances of our Class factory that is dedicated to creating a specific type of vehicle.

Consider the code below:

In our factory above, we dynamically set the vehicleType to the class field declared in the constructor. Thus during initialization, we can specify which type of vehicle the factory instance would create. And the result of this is that TruckFactory only creates trucks and CarFactory only creates cars.

Factory pattern VS other creation patterns

We have seen above how the factory pattern can help centralize our object creation process by encapsulating all our constructor or class initialization logic.

In this section, we will compare the factory pattern with some creation patterns like constructor and singleton.

Factory pattern vs Constructors

The constructor pattern and factory pattern are similar because they are object creation patterns that return a new object. And in our previous implementation of the vehicle factory, we have seen that a factory pattern can use a constructor or class inside. But the factory pattern is preferred in cases where the object creation process depends on dynamic factors — such as when we want to dynamically create subclasses. In some cases, however, this distinction is not clear.

Consider the code below:

In the code above, the factory and the constructor function do the same thing —they return the same car object. And in this case, the object creation is straightforward so we cannot simply prefer the factory pattern.

However, the difference between both methods is that the factory function above returns a standalone object. Which we can use without any effect on the other objects. That is a singleton. Constructors however create objects that have links to their creator.

We will elaborate on this with the code below:

In the code above, we see that both car1 and car2 are standalone objects with no connection with each other. Thus by setting car2.toggleEngine = undefined, car1.toggleEngine still works perfectly. But this is not so with objects created by constructors.

Consider the code below:

In the code above, we see that the objects car1 and car2 that are created by the constructor, are linked to the constructor. And modifying the object prototype can these objects.

This is because the constructor uses JavaScript’s prototypal inheritance under the hood to share logic between all the objects it creates.

The prototype of an object created by a constructor is the prototype of the constructor because of this rule car1 and car2 share the same prototype which contains the toggleEngine method. Thus by setting the toggleEngine to undefineCar.prototype.toggleEngine = undefined;, the code breaks when we try to invoke it from car1.

The main takeaway is that with a factory that returns a singleton, we get complete freedom on the object to return. Also, in complex cases involving multiple constructors or classes, we can dynamically choose one constructor or class based on the set configurations.

On the contrary, a constructor basically instantiates an object with a specific prototype — the constructor’s prototype. And we can extend this prototype later in the body of the constructor function.

Thus constructors are best used for memory management systems as they only declare one set of functions that are attached to the prototype.

Also, constructors are best when the object creation process does not involve dynamic conditions.

Factory pattern vs Singleton pattern

Although the factory pattern can return a singleton, it differs from the singleton pattern because the singleton pattern ensures you always get back the same instance regardless of the type of object you are creating. In a singleton pattern, calls go through the same instance. However, the factory pattern as we have seen enables us to dynamically return different instances depending on the type of object being created.

When To Use A Factory Pattern

  • When our object creation process involves a high level of complexity
  • When we need to return different instances of an object depending on some dynamic factors or application configuration
  • When a class can’t determine the subclass it must create
  • When we need to create different small objects that share some properties

Pros of the factory pattern

  • Promotes loose decoupling by separating the object creation from its implementation
  • Enables us to create different instances based on conditions
  • Promotes code reusability and maintainability
  • Encapsulates the constructor or class and exposes only a defined interface.

Cons of the factory pattern

  • Can be complex to implement in some cases
  • Depending on the complexity can be difficult to test because of the level of abstraction it introduces.

Conclusion

The factory pattern is a very powerful object creation mechanism. And it is particularly useful in complex object creation processes that involve dynamic factors

They promote loose decoupling by separating the object creation from its implementation.

And they encapsulate the constructor or class and expose a defined interface to the client.

Factor patterns also foster code reusability and help keep our logic clean, concise, and maintainable.

After going through this article, I do hope you are ready to start using this pattern in your code.

Even after adopting the Factory Pattern, you should closely monitor how it impacts the performance of your code and make your products provide a better experience for your users.

Even if you feel like the proper decisions have been made, it’s always necessary to verify that this is indeed true and your users have a great experience with your product.

A solution like SessionStack will help you determine and further optimize the experience of your users by allowing you to replay their journeys as videos, showing you how your users actually experience your product. You can quickly determine whether your product is performing according to their expectations or not. In case you see that something is wrong, you can explore all of the technical details from the user’s browser such as the network, debug information, and everything about their environment so that you can easily understand the problem and resolve it.

With what we have covered thus far, I sure hope you are well equipped to start building modular applications.

There is a free trial if you’d like to give SessionStack a try.

SessionStack replaying a session

If you missed the previous chapters of the series, you can find them here:

--

--