Factory Method
Creational Design Patterns
Imagine you're designing a logistics management application. Your core module needs to create Transport
objects to deliver goods. Initially, the company only uses trucks. So, you might write your code like this:
public class LogisticsService {
public void planDelivery(Cargo cargo) {
// ... some logic to plan the route
// The problem is here: direct coupling to a concrete class
Truck truck = new Truck();
truck.load(cargo);
truck.dispatch();
}
}
This code is tightly coupled. The LogisticsService
knows explicitly about the Truck
class. What happens when the business expands to use ships for overseas delivery? You would have to go back into the LogisticsService
, add an if-else
statement (if (type == 'land') new Truck() else if (type == 'sea') new Ship()
), and modify its core logic.
This violates the Open/Closed Principle. The high-level LogisticsService
is not closed for modification when a new transport type is introduced. How can we decouple the LogisticsService
from the specific type of transport it needs to create?
The Pattern: Let Subclasses Decide
Factory Method says: the parent class defines when an object is needed, but each child class decides what object to build.
Instead of calling new Truck()
(or new Ship()
) in the parent, the parent calls an overridable method—its factory method.
Every child overrides this method and returns the product it knows about.
Because creation happens in the child, you can add new products by adding new subclasses, without touching existing code. That’s why the pattern is sometimes called a “virtual constructor.”
The Participants
The Factory Method pattern has four key components:
- Product: The interface or abstract class for the objects the factory method creates (e.g.,
ITransport
). - Concrete Product: The actual classes that implement the
Product
interface (e.g.,Truck
,Ship
). - Creator: An abstract class that declares the factory method, which returns an object of the
Product
type. It contains the core business logic that relies on theProduct
, but it doesn't know which concrete product to create. - Concrete Creator: Subclasses that override the factory method to return an instance of a specific
Concrete Product
.
Refactoring The Logistics App
Let's refactor our LogisticsService
to use the Factory Method pattern.
Step 1: Define the Product Interface This defines the contract for any type of transport.
public interface ITransport {
void load(Cargo cargo);
void dispatch();
}
// Concrete Products
public class Truck implements ITransport { /* ... implementation ... */ }
public class Ship implements ITransport { /* ... implementation ... */ }
Step 2: Create the Abstract Creator with the Factory Method
The Logistics
creator class contains the high-level business logic. It knows it needs some kind of transport, but delegates the actual creation to its subclasses via the abstract createTransport()
factory method.
public abstract class Logistics {
// This is the Factory Method. It's abstract, forcing subclasses to implement it.
public abstract ITransport createTransport();
// This is the core business logic. It is not coupled to any concrete transport type.
// It works with any object that satisfies the ITransport interface.
public void planDelivery(Cargo cargo) {
// ... some logic to plan the route
ITransport transport = createTransport(); // Call the factory method to get the object
transport.load(cargo);
transport.dispatch();
}
}
Step 3: Create Concrete Creators Each concrete creator knows which specific product to instantiate.
public class RoadLogistics extends Logistics {
@Override
public ITransport createTransport() {
// This creator is responsible for creating Trucks.
return new Truck();
}
}
public class SeaLogistics extends Logistics {
@Override
public ITransport createTransport() {
// This creator is responsible for creating Ships.
return new Ship();
}
}
Step 4: Using the Factory in the Client Code
Now, the client (e.g., the main application class) can decide which kind of logistics it needs at runtime. The client is coupled to the concrete creator, but the creator's core logic remains decoupled from the concrete product.
public class Application {
private static Logistics logistics;
public static void main(String[] args) {
// Read a configuration file, get user input, etc.
String transportType = "sea"; // Or "road"
if (transportType.equalsIgnoreCase("road")) {
logistics = new RoadLogistics();
} else if (transportType.equalsIgnoreCase("sea")) {
logistics = new SeaLogistics();
} else {
throw new IllegalArgumentException("Unknown transport type");
}
// The client code works with the abstract creator.
logistics.planDelivery(new Cargo());
}
}
Now, the client code can decide which kind of logistics it needs, and the correct transport will be created automatically, without the Logistics
base class ever knowing the details.
Factory Method vs. Simple Factory
A common source of confusion is the difference between the Factory Method pattern and a "Simple Factory" or "Static Factory". A Simple Factory is a single class with one method that has a large if-else
or switch
statement to create objects.
// This is NOT the Factory Method pattern. It's a "Simple Factory".
public class TransportFactory {
public static ITransport createTransport(String type) {
if ("truck".equalsIgnoreCase(type)) {
return new Truck();
} else if ("ship".equalsIgnoreCase(type)) {
return new Ship();
}
// If we add a Plane, we have to modify this method!
return null;
}
}
Key Differences:
- Extensibility: The Factory Method pattern uses inheritance and forces subclasses to provide the implementation, adhering to the Open/Closed Principle. To add a
Plane
, you create a newAirLogistics
class and you don't touch any existing code. In a Simple Factory, you must modify the central factory class, which violates the OCP. - Implementation: Factory Method relies on an abstract method that subclasses implement. Simple Factory is just a concrete class with a static method that returns different types.
While a Simple Factory can be useful for centralizing object creation, the Factory Method is a true Gang of Four pattern that provides much greater flexibility and extensibility in complex systems. Knowing this distinction is a sign of a mature understanding of design patterns.
Interview Focus: Analysis and Trade-offs
Benefits:
- Excellent for SOLID Principles: This is a key point.
- SRP: It moves the responsibility of object creation into a dedicated place (the factory method and its subclasses), separating it from the core business logic of the creator class.
- OCP: It makes the system highly extensible. You can introduce a new
AirLogistics
creator andPlane
product without touching any of the existingLogistics
,RoadLogistics
, orSeaLogistics
classes.
- Decoupling: The creator class is completely decoupled from the concrete products, depending only on their common interface.
Drawbacks:
- Increased Complexity: The primary disadvantage is that you end up creating a parallel hierarchy of creator classes alongside your product classes. For a simple case, this can feel like overkill.
How Factory Method Relates to Other Concepts
- Relationship to Template Method: The Factory Method pattern is often a specialized version of the Template Method pattern. In our example,
planDelivery()
acts as a Template Method: it defines the skeleton of the delivery algorithm. One of the steps in that algorithm,createTransport()
, is the factory method that subclasses must implement. - Enabling Loose Coupling: This pattern is a fundamental technique for building loosely coupled systems. It allows high-level components to work with abstractions while delegating the creation of specific implementation details to another part of the system.
- Implementing Dependency Inversion: The Factory Method is a way to implement the Dependency Inversion Principle. Our high-level
Logistics
class does not depend on the low-levelTruck
orShip
classes. Instead, both depend on theITransport
abstraction, effectively "inverting" the dependency. - Foundation for More Complex Factories: Understanding the Factory Method is essential before moving on to the more complex Abstract Factory pattern, which uses factory methods to create families of related objects.