Factory Method Design Pattern implementation in Java vs. Javascript

August 28th, 2019

This article demonstrates how Factory Method design pattern can be implemented in Java and Javascript.

Definition

Design Patterns: Elements of Reusable Object-Oriented Software defines the Factory Method as follows:

Define an interface for creating an object, but let sub-classes decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.

Structure

Factory Method Class Diagram

Example - A Car Dealership

Our car dealership application lets a customer order a vehicle from a car dealership. Let's assume that the dealership sells Toyota vehicles and has a well defined workflow of tasks that take place when a customer orders a new vehicle. Now the owner of the dealership has decided to open a new dealership to sell Lexus vehicles. The order processing workflow has to remain consistent between the dealerships. We also have to make sure that correct choices of vehicles are presented for Toyota and Lexus brands.

Creator Class Diagram

Dealership class is the Creator that declares createVehicle() Factory Method. Dealership delegates the responsibility of creating an appropriate Vehicle object to ToyotaDealership and LexusDealership ConcreteCreator sub-classes.

Product Class Diagram

Vehicle is the Product interface (an abstract class in our case). Corolla, Camry, Highlander, ES, IS, and RX are ConrcreteProduct classes that implement the Vehicle interface.

Java

Dealership.java

abstract public class Dealership {
    public void orderVehicle(String type) {
        Vehicle vehicle = createVehicle(type);
        System.out.println("Ordering Vehicle..." + vehicle.getType());
        vehicle.prepareVehicle();
        vehicle.performPaperwork();
        vehicle.acceptPayment();
        vehicle.deliver();
    }

    abstract public Vehicle createVehicle(String type);
} 

Dealership class declares the abstract createVehicle() method. orderVehicle() method defines the order processing workflow. It uses createVehicle() method to get a Vehicle object.

As shown below, ToyotaDealership and LexusDealership classes override the createVehicle() method to instantiate and return an appropriate Vehicle object based on the type of vehicle requested.

ToyotaDealership.java

public class ToyotaDealership extends Dealership {
    public Vehicle createVehicle(String type) {
        switch(type.toLowerCase()) {
            case "corolla": return new Corolla();
            case "camry": return new Camry();
            case "highlander": return new Highlander();
            default: {
                System.out.println("No Such Model available");
                System.exit(0);
                return null;
            }
        }
    }
}

LexusDealership.java

public class LexusDealership extends Dealership {
    public Vehicle createVehicle(String type) {
        switch(type.toLowerCase()) {
            case "es": return new ES();
            case "is": return new IS();
            case "rx": return new RX();
            default: {
                System.out.println("No Such Model available");
                System.exit(0);
                return null;
            }
        }
    }
}

As shown below, Vehicle is an abstract class. It defines various operations that are performed on a vehicle at the time or ordering.

Vehicle.java

abstract class Vehicle {
    String type;

    public String getType() {
        return type;
    }

    public void prepareVehicle() {
        System.out.println("Preparing Vehicle..." + type);
        System.out.println("Perform Inspection...");
        System.out.println("Wash the vehicle...");
        System.out.println("Top up the fuel...");
        System.out.println("Install any accessories - all weather mats, trunk mats, paint protection, etc.");
    }

    public void performPaperwork() {
        System.out.println("Perform Paperwork for..." + type);
        System.out.println("Prepare Invoice...");
    }

    public void acceptPayment() {
        System.out.println("Accept payment...");
    }

    public void deliver() {
        System.out.println("Congratulations! Your " + type + " is ready for you!");
    }
}

Following are the concrete sub-classes that extend the Vehicle class.

class Corolla extends Vehicle {
    public Corolla() {
        type = "corolla";
    }
}

class Camry extends Vehicle {
    public Camry() {
        type = "camry";
    }
}

class Highlander extends Vehicle {
    public Highlander() {
        type = "highlander";
    }
}

class ES extends Vehicle {
    ES() {
        type = "ES";
    }
}

class IS extends Vehicle {
    IS() {
        type = "IS";
    }
}

class RX extends Vehicle {
    RX() {
        type = "RX";
    }
}

Application.java contains the main() method

public class Application {
    public static void main(String[] args) {
        Dealership dealership = new ToyotaDealership();
        dealership.orderVehicle("camry");
    }
}

In the code above, we are creating an instance of ToyotaDealership class and placing an order for Camry. The output looks like this:

amit$ java Application
 
 Ordering Vehicle...camry
 Preparing Vehicle...camry
 Perform Inspection...
 Wash the vehicle...
 Top up the fuel...
 Install any accessories - all weather mats, trunk mats, paint protection, etc.
 Perform Paperwork for...camry
 Prepare Invoice...
 Accept payment...
 Congratulations! Your camry is ready for you!

Javascript

Now let us see how Factory Method design pattern can be implemented in Javascript.

To keep the implementation simple, I have stayed away from the newer javascript syntax and built-in functions available in ES5 and above.

The following code defines the Dealership function constructor. createVehicle argument is a function that gets passed to Dealership. orderVehicle() method is defined on Dealership's prototype.

function Dealership(createVehicle) {
    this.createVehicle = createVehicle;
}
Dealership.prototype.orderVehicle = function(type) {
    var vehicle = this.createVehicle(type);
    if (vehicle === null) {
        console.error('No such vehicle available - ' + type);
        return;
    }
    console.log('Ordering ' + vehicle.getType());
    vehicle.prepareVehicle();
    vehicle.performPaperwork();
    vehicle.acceptPayment();
    vehicle.deliver();
};

The following code defines the Vehicle function constructor and prototype methods.

function Vehicle(type) {
    this.type = type;
}

Vehicle.prototype = {
    getType: function() {
        return this.type;
    },
    prepareVehicle: function() {
        console.log('Preparing ' + this.type);
    },
    performPaperwork: function() {
        console.log('Performing Paperwork for ' + this.type);
    },
    acceptPayment: function() {
        console.log('Accepting payment for ' + this.type);
    },
    deliver: function() {
        console.log('Congratulations! Your ' + this.type + ' is ready!');
    },
};

The following code creates toyotaDealership and lexusDealership objects of type Dealership.

var toyotaDealership = new Dealership(function(type) {
    if (type.toLowerCase() === 'corolla') {
        return new Vehicle('Corolla');
    } else if (type.toLowerCase() === 'camry') {
        return new Vehicle('Camry');
    } else if (type.toLowerCase() === 'highlander') {
        return new Vehicle('Highlander');
    } else {
        return null;
    }
});

var lexusDealership = new Dealership(function(type) {
    if (type.toLowerCase() === 'es') {
        return new Vehicle('ES');
    } else if (type.toLowerCase() === 'is') {
        return new Vehicle('IS');
    } else if (type.toLowerCase() === 'rx') {
        return new Vehicle('RX');
    } else {
        return null;
    }
});

Now we can order a vehicle by calling orderVehicle() method on toyotaDealership and lexusDealership objects.

toyotaDealership.orderVehicle('camry');
 console.log('');
 lexusDealership.orderVehicle('es');

Here's the output

Javascript Output

Summary

Factory Method encapsulates object creation by letting the sub-classes decide what objects to create. In the above example, Dealership defines the process of ordering a vehicle but has no knowledge of the type of vehicle being ordered. The responsibility of creating a vehicle is delegated to the sub-classes.

Source Code

Download