DEV Community

Gaurav
Gaurav

Posted on • Updated on

👷‍♂️ | Decode Factory Pattern | 🏭

When to use

  • To avoid dealing with inconsistent object when the object needs to be created over several steps.
  • To avoid too many constructor arguments.
  • To construct an object that should be immutable.
  • To encapsulate the complete creation logic.

Intent

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


Components

  1. An Interface (or) Abstract class (public)
  2. Set of implementation subclasses (private)
  3. A Factory Method (public)

Structure

Factory Method Pattern


Implementation

1 Create an interface. Clients can code for this interface without worrying about the internal implementation..

package com.gaurav.factorymethod;

public interface Vehicle {
  void design();
  void manufacture();
}
Enter fullscreen mode Exit fullscreen mode

2 Create a set of implementation subclasses. Constructors are protected to prohibit instantiations in clients modules using the 'new' operator.

package com.gaurav.factorymethod;

public class Car implements Vehicle {

  Car() {
    /* constructor is protected.
       clients need to use the factory method */
  }

  @Override
  public void design() {
    System.out.println("Designing Car");
  }

  @Override
  public void manufacture() {
    System.out.println("Manufacturing Car");
  }

}

Enter fullscreen mode Exit fullscreen mode
package com.gaurav.factorymethod;

public class Truck implements Vehicle {

  Truck() {
    /* constructor is protected.
       clients need to use the factory method */
  }

  @Override
  public void design() {
    System.out.println("Designing Truck");
  }

  @Override
  public void manufacture() {
    System.out.println("Manufacturing Truck");
  }

}
Enter fullscreen mode Exit fullscreen mode
package com.gaurav.factorymethod;

public class Motorcycle implements Vehicle {

  Motorcycle() {
    /* constructor is protected.
       clients need to use the factory method */
  }

  @Override
  public void design() {
    System.out.println("Designing Motorcycle");
  }

  @Override
  public void manufacture() {
    System.out.println("Manufacturing Motorcycle");
  }

}
Enter fullscreen mode Exit fullscreen mode

3 Create a class with method 'getVehicle()'. Clients can use this method to create an object instead of using 'new' operator.

package com.gaurav.factorymethod;

public class VehicleFactory {

  /* This is the factory method exposed to the client.
     Client requests for an object by passing the type.
     Client does not need to know about which & how object
     is created internally.
     */
  public Vehicle getVehicle(String vehicleType)
      throws VehicleTypeNotFoundException {

    if (vehicleType == null) {
      return null;
    }

    Vehicle vehicle = null;

    switch (vehicleType) {
      case "car":
        vehicle = new Car();
        break;
      case "truck":
        vehicle = new Truck();
        break;
      case "motorcycle":
        vehicle = new Motorcycle();
        break;
      default:
        throw new VehicleTypeNotFoundException();
    }

    return vehicle;
  }

}

Enter fullscreen mode Exit fullscreen mode

4 The client code. Client knows only the factory method and the interface. Client code does not use 'new' hence decoupled from implementation.

package com.gaurav.client;

import java.util.Scanner;

import com.gaurav.factorymethod.Vehicle;
import com.gaurav.factorymethod.VehicleFactory;
import com.gaurav.factorymethod.VehicleTypeNotFoundException;

public class FactoryMethodClient {

  public static void main(String[] args) {

    Scanner in = new Scanner(System.in);
    String vehicleType = in.nextLine().toLowerCase();

    /* Create a factory instance */
    VehicleFactory factory = new VehicleFactory();

    try {

      /* Create an appropriate vehicle based on the input */
      Vehicle vehicle = factory.getVehicle(vehicleType);

      /* Design and manufacture the vehicle */
      vehicle.design();
      vehicle.manufacture();

    } catch (VehicleTypeNotFoundException e) {
      System.out.println("Invalid vehicle type entered!");
    }

    in.close();
  }

}

Enter fullscreen mode Exit fullscreen mode

Output

[input1]
    MotorCycle
[output1]
    Designing Motorcycle
    Manufacturing Motorcycle

[input2]
    Car
[output2]
    Designing Car
    Manufacturing Car

[input3]
    Bus
[output3]
    Invalid vehicle type entered!
Enter fullscreen mode Exit fullscreen mode

Benefits

  • Loose coupling allows changing the internals without impacting the customer code
  • Factory method provides a single point of control for multiple products
  • Number of instances and their reusability can be controlled with Singleton or Multiton

Drawbacks

An extra level of abstraction makes the code more difficult to read.


Real World Examples

Renting Vehicles. Customer needs to specify only the type of vehicle (car, truck, etc.) that is needed. Customer need not know about the internal details of the vehicle.

Software Example

  • Memcache
  • Filecache
  • Code for SQL standard without worrying about the underlying DB

Java SDK Examples

java.util.Calendar.getInstance()
java.util.ResourceBundle.getBundle()
java.text.NumberFormat.getInstance()
java.nio.charset.Charset.forName()
java.util.EnumSet.of()
javax.xml.bind.JAXBContext.createMarshaller()


Difference between Abstract factory and Factory pattern?

TS;MR:
The Abstract Factory is a set of Factory Methods.

TL;DR:
With the Factory pattern, you produce instances of implementations (Apple, Banana, Cherry, etc.) of a particular interface -- say, IFruit.

With the Abstract Factory pattern, you provide a way for anyone to provide their own factory. This allows your warehouse to be either an IFruitFactory or an IJuiceFactory, without requiring your warehouse to know anything about fruits or juices.


Hope you like it. Would love to hear your thoughts on this design pattern.

Want to discuss more
Lets have a Coffee

Discussion (0)