Creational - Factory Pattern

Imagine you have a toy store, and you want to give kids different types of toys (like cars, dolls, or robots) when they walk in and ask for one. Instead of asking the kid to build the toy by themselves, you have a magic machine. The kid just tells the machine what toy they want by name, and the machine quickly gives them the correct toy—they don’t have to know how the toy is made inside!

Factory Pattern in programming is like that magic toy machine:

  • You don’t need to know how each type of object is built.

  • You just ask the “factory” for what you need, and it gives you the right object.

  • This helps you avoid repeating the creation logic everywhere and keeps things clean and simple.

Factory Pattern is used when we have multiple sub classes (different types of Toys) of a super class (Toy) & based on input (name) we (Toy factory) want to return one class instance.

It provides abstraction between implementation and client class. If there is any change in implementation class name, there is nothing to change from client perspective.

Let's see how ...

Factory Pattern
logisticsType is "road" or "sea"
// AbstractLogistics

public abstract class AbstractLogistics {
	abstract Transport createTransport();
}

// Transport Interface
public interface Transport {
	void delivery();
}

// Truck class
public class Truck implements Transport {
	@Override
	public void delivery() {
		System.out.println("Truck delivers cargo by land");
	}
}

// Ship Class
public class Ship implements Transport {
	@Override
	public void delivery() {
		System.out.println("Ship delivers cargo by Sea");
	}
}

// RoadLogistics
public class RoadLogistics extends AbstractLogistics {

	@Override
	Transport createTransport() {
		return new Truck();
	}
}

// SeaLogistics
public class SeaLogistics extends AbstractLogistics {

	@Override
	Transport createTransport() {
		return new Ship();
	}
}

Now, the factory method, whose role is to provide instance as per the input provided.

public class LogisticsTransportFactory {

    public static Optional<Transport> getTransport(String logisticsType) {
       if ("road".equals(logisticsType)) {
          return Optional.of(new RoadLogistics().createTransport());
       } else if ("sea".equals(logisticsType)) {
          return Optional.of(new SeaLogistics().createTransport());
       }
       return Optional.empty();
    }
// Why Optional? see below code ...
}

Let's see the Client class

public class LogisticsAppClient {

    public static void main(String[] args) {
       Optional<Transport> transportOptional = 
       LogisticsTransportFactory.getTransport("sea");
       transportOptional.ifPresentOrElse(Transport::delivery,
          () -> System.out.println("Invalid business"));
    }
}

//Output : Ship delivers cargo by Sea
// Why Optional ? 
// If the factory can’t create what you asked for, 
// it can return “nothing”—but in a safe way!
// safe way means?
// if it returns null, this may lead to Null Pointer Exception (NPE)
// When you get an Optional, you can easily check 
// if there’s actually a logistics inside or not, 
// and handle it nicely without crashing your program.

So, the client class is only interested in getting instance of a type. It doesn't have any info about how the underlying classes are implemented. It only knows, I will get a Transport instance and I need to call delivery() to get the delivery done.

A textbook example of the factory pattern in the JDK is java.util.Calendar.getInstance().

  • You call Calendar.getInstance() to get a Calendar object. Depending on your environment, locale, or configuration, it may return different concrete Calendar subclasses (like GregorianCalendar).

  • You don’t directly create the concrete class—the factory (getInstance()) decides and gives you the right object.

Advantages of the Factory Pattern:

  • Encapsulates object creation: Clients don’t need to know which concrete class to create; the factory handles the details.

  • Promotes loose coupling: Clients depend only on the factory and interfaces, not on concrete classes.

  • Makes code flexible and extensible: You can add new types/classes without changing client code—just update the factory.

  • Centralizes creation logic: Easier to control, modify, or optimize object creation steps (like caching, configuration, or conditional logic).

Disadvantages of the Factory Pattern:

  • Extra complexity: Adds another layer in the codebase (the factory), which can make the design harder to follow.

  • Possible proliferation of factories: Overusing it can lead to too many factories and small classes.

  • Debugging difficulty: Extra abstraction may make it harder to trace where objects are created or what their types are.

  • May hide concrete type: Sometimes you want direct access to the exact class, but the factory hides it behind an interface.

Last updated

Was this helpful?