Sign in
Log inSign up

Factory Method Pattern

Babatunde Ololade's photo
Babatunde Ololade
ยทAug 11, 2021ยท

7 min read

Factory method is a creational design pattern that provides an interface for creating objects in a superclass, but also allows subclasses to alter the types of objects that will be created.

Creational patterns provide various object creation mechanisms, which increase flexibility and reuse of existing code

PROBLEM:

Imagine you are a very good programmer and you have a little sister whom you love so dearly(let's assume her name is Chayil) . Recently you just got a new job with Microsoft in California, USA; so you have to leave your country Nigeria. But your main problem is you can't do without a day without hearing the voice of your sister. So after you left your country you always make sure you call your little sister every night before you go to bed. But you noticed something, every time you call her the network is always bad; you hardly hear her voice. You end up wasting a lot of time without enjoying your conversation with your dear little sister; Chayil. Because of this, you decided to go to Google Playstore to download the best calling app on the store but you still realized you are still not enjoying your conversation with your dear little sister that much. Then one fateful day night you couldn't connect with your sister at all because the so called best app on Playstore wasn't so good. Because you are a very good programmer you decided to create your own voice calling app just to have a good conversation with your dear little sister. You sent the apk for your sister to install. The app was so good, you end up sleeping late most nights just because you enjoyed the conversation of you and your dear little sister.

After some time you decided to put the app on Google Playstore. After just a month your app had 250K downloads and a very good rating of 4.2/5.0. But there is just one thing stopping people from giving your app a full rating; they said they can't make a video call with it. There's a lot of plea on all major social media platform for you to add a video call functionality to the app. Most users are like they are ready to pay for this feature. You thought of this through then you decided to add a video call functionality to the app.

Great news, right? But how about the code? At present, most of your code is coupled to a VoiceCall class. Adding a VideoCall class into the app would require making changes to the entire codebase. Moreover, if later you decide to add another type of calling to the app, you will probably need to make all of these changes again.

As a result, you will end up with pretty nasty code, riddled with conditionals that switch the appโ€™s behavior depending on the class of calling objects.

SOLUTION:

Factory Method Pattern to the rescue ๐Ÿ˜‹๐Ÿ˜‹๐Ÿ˜‹

The Factory Method patten suggests that you replace direct object construction call (using the new operator) with calls to a special Factory Method. These objects are still created via the new operator, but it's being called from within the Factory Method. The objects returned by a factory method are often referred to as products

Now how does this apply to our calling app? Instead of creating another VideoCall class we could have a class called Call which contains most of the functionalities of our app and then have two classes called VideoCall and AudioCall which are subclasses of this class. How will this work?

aaaaaaa.jpeg

We can create an abstract class Call which has an abstract method called factoryMethod. And also do some stuffs which are related to a general calling app. This is how we could create the abstract class using Typescript

abstract class Call {
    /**
     * Note that the Creator may also provide some default implementation of the
     * factory method.
     */
    public abstract factoryMethod(): Product; // what a factoryMethod returns is known as a product

    /**
     * Also note that, despite its name, the Creator's primary responsibility is
     * not creating products. Usually, it contains some core business logic that
     * relies on Product objects, returned by the factory method. Subclasses can
     * indirectly change that business logic by overriding the factory method
     * and returning a different type of product from it.
     */
    public someOperation(): string {
        // Call the factory method to create a Product object.
        const product = this.factoryMethod();
        // Now, use the product.
        return `Creator: The same creator's code has just worked with ${product.makeCall()}`;
    }
}

In Factory Method pattern the Call class will be regarded as a Creator. And what is returned from it's factory method is called a product.

Now other classes can extend and overried the Creator factoryMethod in order to change the resulting type

class VideoCall extends Call {
    /**
     * Note that the signature of the method still uses the abstract product
     * type, even though the concrete product is actually returned from the
     * method. This way the Creator can stay independent of concrete product
     * classes.
     */
    public factoryMethod(): Product {
        return new VideoCallProduct();
    }
}

class VoiceCall extends Call {
    public factoryMethod(): Product {
        return new VoiceCallProduct();
    }
}

Even though the subclasses of our Creator can change the resulting type of our Creator factory method there are still some limitations. Subclasses may return different types of products only if these products have a commonbase class or interface. Also, the factory method in the baseclass (which is the creator) should have its return type declared as this interface. So we can create an interface Product that has a method called makeCall which all factoryMethods must return.

/**
 * The Product interface declares the operations that all concrete products must
 * implement.
 */
interface Product {
    makeCall(): void;
}

Now we can create a class for our concrete creators (subclasses of our parent class Creator) which each factoryMethod of our concrete creators can return

/**
 * Concrete Products provide various implementations of the Product interface.
 */
class VideoCallProduct implements Product {
    public makeCall() {
        return '{Connects to some external api and make a video call}';
    }
}

class VoiceCallProduct implements Product {
    public makeCall() {
        return '{Make a voice call with a plugin}';
    }
}

The factory method in the VideoCall class returns videoCallProducts, whereas the factory method in the VoiceCall class returns voiceCallProducts.

The code that uses the factory method (often called the client code ) doesnโ€™t see a difference between the actual products returned by various subclasses. The client treats all the same way by calling the method they all implements.


/**
 * The client code works with an instance of a concrete creator, albeit through
 * its base interface. As long as the client keeps working with the creator via
 * the base interface, you can pass it any creator's subclass.
 */
function clientCode(creator: Call) {
    // ...
    console.log('Client: I\'m not aware of the creator\'s class, but it still works.');
    console.log(creator.someOperation());
    // ...
}

/**
 * The Application picks a creator's type depending on the configuration or
 * environment.
 */
console.log('App: Making a video call.');
clientCode(new VideoCall());
console.log('');

console.log('App: Making a voice call.');
clientCode(new VoiceCall());

Now you can launch the second version of your app without actually changing all your whole app structure. Thanks to the Factory Method Pattern.

Now let's say again after launching the second version of your app you hit a 5M downloads on Playstore and users are now requesting for a hologram feature. You don't need to change the Call class you can just create a Hologram class that extends the Call base class and then write the necessary codes required. By doing this, we are following a very popular principle in Software Engineering called the Open and Close Principle. Now our code is open to expansion and also close to modification. The other principle we are following here is Single Responsibility Principle. We can move the product creation code into one place in the program, making the code easier to support.