Taha

Create objects with factory functions

The main power of OOP (object-oriented programming) comes from polymorphism. But to enable polymorphism you need to use factories to create objects. Here's why.

The basic goal of polymorphism is to be able to substitute one object with another without changing the client code—the client code is the code uses that object.

The client code doesn't have to know the implementation of the object its using. If, for example, your client code expects to deal with a user object, then it just uses it as a user object regardless of the specific type of that user. The user object can either be an instance of AdminUser or RegularUser class.

Example

Let's say that in your app you have a list of users that you can manage. Next to each user, there's a delete button to delete the user account. If you try to delete a user of type admin, then you should see an error saying that an admin can't be deleted.

In this code example, I have a function called deleteUser that takes a user object that I want to delete.

function deleteUser(user) {
    try {
        user.delete()
        console.log('user is deleted successfully')
    } catch (error) {
        // Display the error message to the user
        console.log(error.message)
    }
}

The user object in the parameter can either be an instance of RegularUser or AdminUser class.

class User {
    deleteUser() {}
}

class RegularUser extends User {
    deleteUser() {
        // Code to delete the user
    }
}

class AdminUser extends User {
    deleteUser() {
        throw new Error('An admin user cannot be deleted')
    }
}

The question is: where do you decide if the user passed to deleteUser function is an admin or a regular user? The answer is using a factory function.

What is a factory function?

A factory function is a function that creates an object and then returns it to the caller.

To create an object without a factory function, you need to write this:

const regularUser = new RegularUser()
const adminUser = new AdminUser()

But to create it with a factory function, you would write this:

function createUser(userType) {
    if (userType === 'admin') {
        return new AdminUser()
    }

    return new RegularUser()
}

Not only are factory functions responsible for creating the object but also choosing which one to create.

It's up to you to define how to choose an object. It might be based on some flag (like this example), or based on some environment variable, or maybe you pass it the user data that you fetched from the database and make the choice based on some fields in that user data.

You can put as many variants of that object as you want, but the main thing to remember here is that each of these variants should have the same expected interface—because that's what polymorphism is all about.

As a rule thumb, you should always use a factory function to create objects even if you only have a single variant of that object. It may start with one type, but if you decided to support more types in the future, then you would just need to modify its factory function code—instead of updating all the places that creates the object.

Taha Shashtari

I'm Taha Shashtari, a full-stack web developer. Building for the web is my passion. Teaching people how to do that is what I like the most. I like to explore new techniques and tools to help me and others write better code.

Subscribe to get latest updates about my work
©2024 Taha Shashtari. All rights reserved.