Hello! Welcome to your next lesson in our Creational Design Patterns module.
In our last session, we explored the Singleton pattern, focusing on how to guarantee a single instance of a class, especially in a concurrent environment. We saw that creational patterns are all about managing object instantiation in a controlled way.
Today, we'll shift our focus from controlling the number of instances to controlling which type of instance is created. We'll be covering the Factory Method pattern. Your goal for this lesson is to learn how to apply this pattern to delegate object creation to subclasses. This is a cornerstone pattern for building flexible and extensible systems, directly supporting the Open/Closed Principle we covered in the SOLID module.
The Problem: When You Don't Know Which Class to Create
Imagine you're building a logistics application. Your core logic needs to schedule deliveries, but the actual transport could be a Truck, a Ship, or a Plane. A simple but rigid approach would be to have a central piece of code with a large if-else or switch statement:
// Anti-pattern: Violates Open/Closed Principle
public Transport createTransport(String type) {
if (type.equals("TRUCK")) {
return new Truck();
} else if (type.equals("SHIP")) {
return new Ship();
} else if (type.equals("PLANE")) {
return new Plane();
}
// ...what if we add a Drone? We have to modify this code!
return null;
}
This code is brittle. Every time a new method of transport is introduced, you have to modify this central creation logic. This violates the Open/Closed Principle because the class is not closed for modification.
The Factory Method pattern offers an elegant solution: Let subclasses decide which object to instantiate.
The Solution: Delegating Creation
The Factory Method pattern defines an interface or abstract class for creating an object but lets subclasses alter the type of objects that will be created.
The core idea is to replace direct constructor calls (e.g., new Truck()) with a call to a special "factory" method. The magic is that this factory method is abstract (or can be overridden) in the parent class, and concrete subclasses provide the implementation, each returning a different specific product.
Structure of the Factory Method Pattern
To understand the structure, let's look at the four key components involved.

- Product: This is an interface or abstract class that defines the object the factory method will create. In our logistics example, this would be a
Transportinterface with adeliver()method. - ConcreteProduct: These are the specific classes that implement the Product interface. For example,
Truck,Ship, andPlanewould be ConcreteProducts. - Creator: This is an abstract class that declares the factory method, which returns an object of the Product type. This class might also have other methods that use the product created by the factory method. For instance, a
Logisticsclass could have an abstractcreateTransport()method and a concreteplanDelivery()method that calls it. - ConcreteCreator: These classes subclass the Creator and override the factory method to return an instance of a specific ConcreteProduct. For example,
RoadLogisticswould implementcreateTransport()to return anew Truck(), whileSeaLogisticswould return anew Ship().
This structure decouples the client code (which might be in the Creator class itself, like planDelivery()) from the concrete implementation of the products. The Logistics class can plan a delivery without ever knowing if it's using a Truck or a Ship.
A Practical Java Example: Vehicle Manufacturing
Let's solidify this with a code example. We'll use a vehicle manufacturing scenario, which is clearly explained in the Baeldung article "The Factory Design Pattern in Java."
The Factory Design Pattern in Java
This article provides a step-by-step implementation of the Factory Method pattern. We will use its vehicle example to see how the four components work together in code.
Read sections 2 through 4 (up to 'Abstract Factory Pattern'). Focus on how the code implements each of the four roles we just discussed: MotorVehicle (Product), Car/Motorcycle (ConcreteProducts), MotorVehicleFactory (Creator), and CarFactory/MotorcycleFactory (ConcreteCreators).
Let's break down the implementation based on the article:
-
Product Interface (
MotorVehicle):public interface MotorVehicle { void build(); }This defines the common contract for all vehicles we can build.
-
Concrete Products (
MotorcycleandCar):public class Motorcycle implements MotorVehicle { @Override public void build() { System.out.println("Build Motorcycle"); } } public class Car implements MotorVehicle { @Override public void build() { System.out.println("Build Car"); } }These are the specific objects we want to create.
-
Abstract Creator (
MotorVehicleFactory):public abstract class MotorVehicleFactory { public MotorVehicle create() { // This method contains logic that uses the product. // It calls the factory method to get a product object. MotorVehicle vehicle = createMotorVehicle(); vehicle.build(); return vehicle; } // This is the factory method! It's abstract. protected abstract MotorVehicle createMotorVehicle(); }Notice the
create()method. It defines the general process but delegates the specific instantiation step to thecreateMotorVehicle()method. This is the heart of the pattern. -
Concrete Creators (
MotorcycleFactoryandCarFactory):public class MotorcycleFactory extends MotorVehicleFactory { @Override protected MotorVehicle createMotorVehicle() { return new Motorcycle(); } } public class CarFactory extends MotorVehicleFactory { @Override protected MotorVehicle createMotorVehicle() { return new Car(); } }Each subclass provides its own implementation of the factory method, deciding which concrete product to return.
Now, the client code can decide which factory to use, and the rest of the logic follows without change:
// Client code
MotorVehicleFactory carFactory = new CarFactory();
carFactory.create(); // Output: Build Car
MotorVehicleFactory motorcycleFactory = new MotorcycleFactory();
motorcycleFactory.create(); // Output: Build Motorcycle
We can now add an ElectricCarFactory or a ScooterFactory without ever touching the MotorVehicleFactory class or the client code that uses it. Our system is open for extension.
Test your understanding!
You are asked to design a notification system that can send emails, SMS messages, and push notifications. The client code should not be tightly coupled to the specific notification channel (EmailSender, SmsSender, etc.).
How would you apply the Factory Method pattern? Identify the four key roles (Product, ConcreteProduct, Creator, ConcreteCreator) in your design.
Show answer
- Product: An interface, say
Notification, with asend(message)method. - ConcreteProducts: Classes like
EmailNotification,SmsNotification, andPushNotificationthat implement theNotificationinterface. - Creator: An abstract class
NotificationFactorywith an abstract methodcreateNotification()and perhaps a common method likesendNotification(message)which uses the created object. - ConcreteCreators: Subclasses like
EmailFactory(overridescreateNotificationto returnnew EmailNotification()),SmsFactory, andPushFactory.
Factory Method in the Wild: Core Java Libraries
The Factory Method pattern is not just a theoretical concept; it's widely used in core Java libraries. You've likely used it without even realizing it. The Refactoring.guru article provides a good list.
Factory Method in Java / Design Patterns
Let's look at where this pattern appears in the JDK. This will help you recognize it in existing codebases.
Read the short section titled 'Usage examples'. Note the examples like java.util.Calendar#getInstance() and java.text.NumberFormat#getInstance().
Many of these are a slight variation called a Static Factory Method. In these cases, the class has a static method that creates and returns instances of itself or its subclasses. For example, Calendar.getInstance() returns a GregorianCalendar or another calendar type based on your locale and timezone. You, as the client, don't need to know the concrete class name; you just ask the Calendar class for an appropriate instance.
While not the full pattern with a Creator hierarchy, it shares the same spirit: encapsulating object creation logic and decoupling the client from concrete types.
Connection to Spring Framework
In your work with Spring Boot, the IoC (Inversion of Control) container handles most object creation for you. When you define a bean, Spring acts as a highly sophisticated factory.
However, the Factory Method pattern is still very relevant within your Spring components. For instance, you might have a @Service that needs to create different strategy objects based on runtime parameters. You could implement a factory method inside that service to handle this creation logic cleanly, without cluttering your business methods with new keywords and if-else blocks.
Conclusion
Today we explored the Factory Method pattern, a powerful tool for decoupling a client from the concrete classes it needs to create.
Key Takeaways:
- Purpose: To define an interface for creating an object, but let subclasses decide which class to instantiate.
- Problem Solved: It avoids tight coupling between the client and concrete product classes, making the system more modular and easier to extend, thus upholding the Open/Closed Principle.
- Key Components: Product, ConcreteProduct, Creator, and ConcreteCreator.
- Benefit: The code in the Creator class works with the abstract Product interface and is completely independent of the ConcreteProduct implementations.
In our next lesson, we will build upon this concept to tackle an even more complex creation problem with the Abstract Factory pattern. Think about our GUI example: what if creating a WindowsButton also meant we needed a WindowsCheckbox and a WindowsTextField? The Factory Method creates a single product, but Abstract Factory is designed to create families of related products.