Creational - Abstract Factory Pattern
Pre-requisite : Read Creational - Factory Pattern
Now, suppose, apart from Transport object, there is another object called Handler . The handler can be WarehouseHandler for Road Logistics and PortHandler for Sea Logistics.
How do we implement similar to factory pattern?

Here,
Related Products: Each factory ( RoadLogistics & SeaLogistics ) creates a family of related objects that work together (Truck + WarehouseHandler, Ship + PortHandler)
Abstract Factory Pattern: Each factory produces multiple related products specific to its logistics environment
// Abstract Products - Transport and Handling
interface Transport {
void deliver();
}
interface Handler {
void handle();
}
// Concrete Products for Road Logistics
class Truck implements Transport {
@Override
public void deliver() {
System.out.println("Delivering cargo by truck on road");
}
}
class WarehouseHandler implements Handler {
@Override
public void handle() {
System.out.println("Loading/unloading cargo at warehouse");
}
}
// Concrete Products for Sea Logistics
class Ship implements Transport {
@Override
public void deliver() {
System.out.println("Delivering cargo by ship across ocean");
}
}
class PortHandler implements Handler {
@Override
public void handle() {
System.out.println("Loading/unloading cargo at port with cranes");
}
}
// Abstract Factory Interface
interface LogisticsFactory {
Transport createTransport();
Handler createHandler();
}
// Concrete Factory for Road Logistics
class RoadLogisticsFactory implements LogisticsFactory {
@Override
public Transport createTransport() {
return new Truck();
}
@Override
public Handler createHandler() {
return new WarehouseHandler();
}
}
// Concrete Factory for Sea Logistics
class SeaLogisticsFactory implements LogisticsFactory {
@Override
public Transport createTransport() {
return new Ship();
}
@Override
public Handler createHandler() {
return new PortHandler();
}
}
// Client code that uses the factory
class LogisticsCompany {
private Transport transport;
private Handler handler;
public LogisticsCompany(LogisticsFactory factory) {
transport = factory.createTransport();
handler = factory.createHandler();
}
public void executeDelivery() {
System.out.println("=== Starting Delivery ===");
handler.handle();
transport.deliver();
System.out.println();
}
}
// Demo
public class LogisticsDemo {
public static void main(String[] args) {
// Road Logistics
System.out.println("Road Logistics:");
LogisticsFactory roadFactory = new RoadLogisticsFactory();
LogisticsCompany roadCompany = new LogisticsCompany(roadFactory);
roadCompany.executeDelivery();
// Sea Logistics
System.out.println("Sea Logistics:");
LogisticsFactory seaFactory = new SeaLogisticsFactory();
LogisticsCompany seaCompany = new LogisticsCompany(seaFactory);
seaCompany.executeDelivery();
}
}
Output
Road Logistics:
=== Starting Delivery ===
Loading/unloading cargo at warehouse
Delivering cargo by truck on road
Sea Logistics:
=== Starting Delivery ===
Loading/unloading cargo at port with cranes
Delivering cargo by ship across ocean
Advantages
Isolation of concrete classes:
Because a factory encapsulates the responsibility and the process of creating product objects, it isolates clients from implementation classes.
Clients manipulate instances through their abstract interfaces. Product class names are isolated in the implementation of the concrete factory; they do not appear in client code.
Promoting consistency among products:
When product objects in a family are designed to work together, it’s important that an application use objects from only one family at a time. AbstractFactory makes this easy to enforce.
Disadvantages
Complexity:
Abstract Factory can introduce additional complexity to the codebase.
Having multiple factories and abstract product interfaces may be overkill for simpler projects.
Rigidity with New Product Types:
Suppose you want to add a third product (e.g.,
TrackingDevice) to every logistics family (RoadLogistics,SeaLogistics).This means:
Every factory interface and every concrete factory class needs to be updated to add
createTrackingDevice().You must implement
TrackingDevicefor every family.
It’s a big change that touches many classes—risking errors and making refactoring time-consuming.
Difficult to test
Testing individual product objects outside the factory context may require mock factories or additional setup.
Rapid prototyping is harder because you must implement full product families even for simple use cases.
Then how to choose?
The best solution depends on your requirements—you don’t always need (or want) the full weight of the Abstract Factory pattern. Here are some commonly used alternatives and suggestions for different needs:
Only one product per scenario
Simple Factory / Factory Method
Complicated object setup with many parameters
Builder Pattern
Spring or large-scale application
Dependency Injection (Spring DI)
Want plugins/extensions loaded at runtime
Registry-based Factory
Need strict families of related products
Abstract Factory
Last updated
Was this helpful?