DEV Community

Colum Ferry
Colum Ferry

Posted on

The Factory Pattern - Design Patterns meet the Frontend

Picture this. A Car Dealership selling Cars 🚗. Suddenly, they want to branch out and sell Trucks 🚛. You had initially programmed the order and sale system to handle Cars. What do you do now? Do you duplicate the majority of the business logic in the system to also handle trucks specifically?

Sure, it's a somewhat quick win to do this. A short while later, the Dealership decides it's gonna start selling Motorbikes.

🤦 Oh no. More code duplication? What happens if the order system needs to change, do we need to update it now in three places!!?

We've all been there. It's hard to predict when this situation might occur. But when it does, know that there is a solution that initially might require a bit of refactoring but will surely mean a more maintainable solution, especially when the Dealership says it's gonna start selling Boats! 🛥️

In this article, we will discuss:

  • 💪 A Solution - The Factory Pattern
  • 🤔 When should I use it?
  • 🤯 Some Advantages and Disadvantages
  • ❓ Where is it being used in the Frontend World?
  • 🏭 Let's see an example!

💪 A Solution - The Factory Pattern

The Factory Pattern is a creational design pattern that adds an abstraction layer over common base behaviour between multiple objects of a generic type.
The client code, the code that will use this layer, does not need to know the specifics of the implementation of the behaviour, as long as it exists.

If we take our Car Dealership turned Multi-Vehicle Dealership example above we can see that the common ground between the Cars, Trucks and Boats are that they are all Vehicles. The Order System within the Dealership only needs to work with a base Vehicle, it doesn't need to know the specifics about the Vehicle being processed.

Let's take a quick look at a UML Diagram to illustrate this:

The Factory Pattern

As we can see from the diagram, the system contains concrete implementations of the Vehicle interface. The OrderSystem doesn't know, or need to know, what these concrete implementations are, it simply relies on the VehicleFactory to create and return them when required, therefore decoupling our OrderSystem from the Vehicles the Dealership wants to sell! 🚀🚀🚀

They can branch out to as many Vehicles as they like now and we only ever have to create a new implementation of the Vehicle interface and update our VehicleFactory to create it! 🔥🔥🔥

🤔 When should I use it?

There are a few situations, other than the one describe above where this pattern fits perfectly:

  • Any situation where at or during runtime you do not know the exact type or dependency a specific portion of your code needs to work with.
  • If you are developing a library, using the Factory Pattern allows you to provide a method for consuming developers to extend its internal components without requiring access to the source itself!
  • If you need to save system resources, you can use this Pattern to create an Object Pool, where new objects are stored when they do not already exist, and will be retrieved from when they do exist, instead of creating a new one.

🤯 Some Advantages and Disadvantages

Advantages:

  • It avoids tight coupling between the Consumer of the Factory and the Concrete Implementations.
  • In a way it meets the Single Responsibility Principle by allowing the creation code to be maintained in one area.
  • It also meets the Open/Closed Principle by allowing new Concrete Implementations to be added without breaking the existing code.

Disadvantages:

  • It can increase the complexity and maintainability of the codebase as it requires a lot of new subclasses for each Factory and Concrete Implementation

❓ Where is it being used in the Frontend World?

Surprisingly (well maybe not), Angular allows the usage of Factories in their Module Providers. Developers can provide dependencies to modules using a factory, which is extremely useful when information required for the provider is not available until Runtime.

You can read more about them on the Angular Docs for Factory Providers.

🏭 Let's see an example!

A great example for this in the Frontend is cross-platform UIs.

Imagine having a cross platform app that shows a Dialog Box. The app itself should allow a Dialog to be rendered and hidden. The Dialog can render differently on a mobile app than it does on desktop. The functionality however, should be the same. The app can use a factory to create the correct Dialog at runtime.

For this example, we are going to use TypeScript to create two implementations of a Dialog, a MobileDialog and a DesktopDialog. The app will use the User Agent String to determine if the app is being viewed on a desktop or mobile device and will use the Factory to create the correct Dialog.

Note: Normally, it is more ideal to develop one responsive Dialog, however, this is an example to illustrate the Factory Pattern.

Let's start by creating a base Dialog Interface

interface Dialog {
    template: string;
    title: string;
    message: string;
    visible: boolean;

    hide(): void;
    render(title: string, message: string): string;
}
Enter fullscreen mode Exit fullscreen mode

This interface defines the common behaviour and state that any Concrete Implemenation will adhere to.
Let's create these Concrete Implementations:

class MobileDialog implements Dialog {
    title: string;
    message: string;
    visible = false;

    template = `
        <div class="mobile-dialog">
            <h2>${this.title};</h2>
            <p class="dialog-content">
              ${this.message}
            </p>
        </div>
    `;

    hide() {
        this.visible = false;
    }

    render(title: string, message: string) {
        this.title = title;
        this.message = message;
        this.visible = true;

        return this.template;
    }
}

class DesktopDialog implements Dialog {
    title: string;
    message: string;
    visible = false;

    template = `
        <div class="desktop-dialog">
            <h1>${this.title};</h1>
            <hr>
            <p class="dialog-content">
              ${this.message}
            </p>
        </div>
    `;

    hide() {
        this.visible = false;
    }

    render(title: string, message: string) {
        this.title = title;
        this.message = message;
        this.visible = true;

        return this.template;
    }
}
Enter fullscreen mode Exit fullscreen mode

There are only slight variations in this functionality, and you may argue an Abstract Class would be a better fit for this as the render and hide methods are the same. For the sake of this example, we will continue to use the Interface.

Next we want to create our Factory:

class DialogFactory {
    createDialog(type: 'mobile' | 'desktop'): Dialog {
        if (type === 'mobile') {
            return new MobileDialog();
        } else {
            return new DesktopDialog();
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Our Factory takes a type and will subsequently create the correct implementation of the Dialog.

Finally our App needs to consume our Factory:

class App {
    dialog: Dialog;
    factory = new DialogFactory();

    render() {
        this.dialog = this.factory.createDialog(isMobile() ? 'mobile' : 'desktop');
        if (this.dialog.visible) {
            this.dialog.render('Hello World', 'Message here');
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Looking at the code above, we can see that the App does not need to know about the Concrete Implementations, rather, that logic is the responsibility of the DialogFactory.

Hopefully, this code example has helped to clarify the Factory Pattern and it's potential usage in the Frontend World.


Personally, I understand the concept and the advantages of this Pattern, but I do not like the focus and reliance on Inheritance that it requires for it's implementation.

Feel free to discuss any other examples or your own opinions of this Pattern, as I'm still undecided on it.
If you have any questions, feel free to ask below or reach out to me on Twitter: @FerryColum.

Top comments (9)

Collapse
 
fly profile image
joon • Edited

A project I recently worked on a project that turned out to really need this kind of structure, but I only realized this when it was a bit too late.
A very informational read, especially the 'when should I use this' part. :)
Thank you dearly

Collapse
 
coly010 profile image
Colum Ferry

You're more than welcome!

Collapse
 
jenc profile image
Jen Chan

Very helpful breakdown and I feel like I’ve come across it in early lessons of OO JS, but especially as applied to front end components, so much easier to understand, thanks

Collapse
 
coly010 profile image
Colum Ferry

You're welcome!

Collapse
 
chase2981 profile image
Chase Gibbons

Good post and all, but could you show us how to create a factory which doesn’t violate the open/closed principle? (I.e. a factory which when adding a third let’s say “PrintDialogue”, or whatever, a factory which doesn’t require modifying the factory’s code just to add another implementation) Maybe something that uses metadata from the children to determine what to do for instance? Thnx.

Collapse
 
coly010 profile image
Colum Ferry

There's a more advanced form of the Factory Pattern which makes it easier to stay aligned with the Open Closed Principle, called the Abstract Factory Pattern: en.wikipedia.org/wiki/Abstract_fac...

However, in the implementation above, while the factory itself violates the open/closed principle, the Dialog itself doesn't and the factory aids you in adhering to it.

You implement the Dialog, thus extending it, rather than modifying it and you use the abstract to interface with the concrete implementation returned from the Factory.

Collapse
 
awakeel profile image
Abdul wakeel

Thank you, This is what I was looking for.

Collapse
 
jannikwempe profile image
Jannik Wempe

Thanks for explanation and the good examples :-)
As you said, you don't like the fact that it relies on inheritance: wouldn't it be possible to create a factory that uses composition instead?

Collapse
 
coly010 profile image
Colum Ferry

I'm not entirely sure. I mean, you could essentially have a bunch of behaviour classes that you piece together within the factory and return that new object, but I feel like that borders a different pattern, The Builder Pattern, potentially.