Factory Design Pattern

In this article let’s discuss in brief about Factory Design Pattern

In an Object Oriented Programming language like java, we usually deal with classes, creating objects for them and executing operations in those objects. To create any object we will use the new keyword and it will work fine. Consider a scenario where you need to create an object based on the given input, from a set of objects available.

Let’s take up an example of an abstract class Car and set of concrete classes extending it.

package in.techdive.factory;

public abstract class Car {
        private int price=0;
        private String color ="";
        private String name="";
       
        public abstract int getPrice();
        public abstract String getColor();
        public abstract String getName();
}

class HondaCity extends Car
{
        @Override
        public String getColor() {
                return "Black";
        }

        @Override
        public String getName() {
                return "HondaCity";
        }

        @Override
        public int getPrice() {
                return 1000000;
        }
 }
 
class HondaCivic extends Car
{
        public String getColor() {
                return "Silver";
        }

        public String getName() {
                return "HondaCivic";
        }

        public int getPrice() {
                return 1100000;
        }
}
 
class HondaAccord extends Car
{
        public String getColor() {
                return "Grey";
        }

        public String getName() {
                return "HondaAccord";
        }

        public int getPrice() {
                return 1800000;
        }        
 }

Suppose we want an instance of a car based on user input ("city", "civic", "accord") then we need a conditional check to create the instance as follows.

Car c = null;

if(name.equalsIgnoreCase("city"))
    c = new HondaCity();
else if(name.equalsIgnoreCase("accord"))
    c = new HondaAccord();
else if(name.equalsIgnoreCase("civic"))
    c = new HondaCivic();

The above code is prone to change, when we create more car instances like HondaJazz, HondaOdessey etc., or if the implementation class or subclass changes. So its better to move this part of the code from client side to another class as follows.

package in.techdive.factory;

public class CarFactory {
        /*
         *  Factory method to return a Car instance based on the given name parameter
         */
   
        public static Car getCar(String name)
        {
                if(name.equalsIgnoreCase("city"))
                        return new HondaCity();
                else if(name.equalsIgnoreCase("accord"))
                        return new HondaAccord();
                else if(name.equalsIgnoreCase("civic"))
                        return new HondaCivic();
                else
                return null;
        }
}

The above class is called the factory class. It is a simple factory just encapsulating the object creation. If there are any changes to be made in the car instances created only this class should be changed and thus avoiding maintenance havoc. This is a simple factory pattern. Have a look at the final client code to access the factory.

package in.techdive.factory;
/**
 * A class used to get a car from factory and simulate test driving it
 */

public class TestDriveSimulation
{
    public static void main(String[] args)
    {
        Car car = CarFactory.getCar("accord");
        System.out.println("Start test driving your -> "+car.getName());
    }
}

As an software application is ready to change, consider a scenario where the client requires cars of other brand like Hyundai, Ford etc., it will be really bad to increase the if-else condition in the getCar() method in Car Factory class.
One better way to handle it is, make it as an abstract class and then create a factory class for each brand. Let the getCar() method be implemented in the subclass factories. Take a look at the following code.

package in.techdive.factory;

public abstract class CarFactory {
    /*
     * Factory method to return a Car instance based on the given name parameter
     */

    public abstract Car getCar(String name);
}

class HondaFactory extends CarFactory
{
        public  Car getCar(String name)
        {
                if(name.equalsIgnoreCase("city"))
                        return new HondaCity();
                else if(name.equalsIgnoreCase("accord"))
                        return new HondaAccord();
                else if(name.equalsIgnoreCase("civic"))
                        return new HondaCivic();
                else
                return null;
        }
}

class HyundaiFactory extends CarFactory
{
        public  Car getCar(String name)
        {
                if(name.equalsIgnoreCase("accent"))
                        return new HyundaiAccent();
                else if(name.equalsIgnoreCase("santro"))
                        return new HyundaiSantro();
                else if(name.equalsIgnoreCase("sonata"))
                        return new HyundaiSonata();
                else
                return null;
        }
}

Similarly as the number of cars (brands) has increased, we have few car classes added.

class HyundaiAccent extends Car
{
        public String getColor() {
                return "Grey";
        }

        public String getName() {
                return "HyundaiAccent";
        }

        public int getPrice() {
                return 800000;
        }
}
 
class HyundaiSantro extends Car
{
        public String getColor() {
                return "Grey";
        }

        public String getName() {
                return "HyundaiSantro";
        }

        public int getPrice() {
                return 400000;
        }
}
 
class HyundaiSonata extends Car
{
        public String getColor() {
                return "Grey";
        }

        public String getName() {
                return "HyundaiSonata";
        }

        public int getPrice() {
                return 1600000;
        }
         
}

Have a look at the improved version TestDriveSimulation class below.

 package in.techdive.factory;

/**
 * Class used to get a car from factory and simulate test driving it
 */

public class TestDriveSimulation
{
    public static void main(String[] args)
    {
        CarFactory cFactory = new HyundaiFactory();
        Car car = cFactory.getCar("accent");
        System.out.println("Start test driving your -> "+car.getName());
    }
}

This is also a factory pattern but usually called as a factory method pattern. It is because here the subclasses of the factory class decide on what is the factory product to be created. In this way we can add more factories for different car brands.
So far we have seen about, factory design pattern with an example. We can also combine Singleton with factory design pattern to restrict the number of instances created.

Technology: 

Search