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 TrackingDevice for 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:

Use case
Best alternative

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?