HexTechie
A web for devs from 0 to F

Abstract Factory Design Pattern

Abstract factory design pattern example in Java.
-- By Jan
March 10, 2022
article image
UML diagram.

Abstract factory design pattern, like a simpler factory design pattern, belongs to creational design patterns. This pattern is similar to factory design pattern with a one core difference. It adds another layer of abstraction, but for factories. So this pattern not only provides an abstraction for creating objects of a concrete type, but also an abstraction to retrieve a factory of a concrete type that can create our desired object. To retrieve a factory we use a factory provider.

First of all, we need an interface that specifies some basic logic our classes must implement and also an interface that describes logic for a factory. Our factory will produce objects of two types: a Vehicle and a Paint for that vehicle. Two concrete factories will be created with one producing a Car and a Red paint and a second one producing a Truck and a Green paint. So let's define our interfaces.

Vehicle.java
        public interface Vehicle {

    String getName();
}
    
Paint.java
        public interface Paint {

    String getName();
}
    

Next we need some implementations of our Vehicle, i.e. Car and Truck and some implementations of our Paint, i.e. Red and Green.

Car.java
        public class Car implements Vehicle {

    @Override
    public String getName() {
        return "Car";
    }
}
    
Truck.java
        public class Truck implements Vehicle {

    @Override
    public String getName() {
        return "Truck";
    }
}
    
Red.java
        public class Red implements Paint {

    @Override
    public String getName() {
        return "Red";
    }
}
    
Green.java
        public class Green implements Paint {

    @Override
    public String getName() {
        return "Green";
    }
}
    

An interface specifying our logic for a factory and implementations of that interface is as follows.

Factory.java
        public interface Factory {

    public Vehicle createVehicle();

    public Paint createPaint();
}
    
CarFactory.java
        public class CarFactory implements Factory {

    @Override
    public Vehicle createVehicle() {
        return new Car();
    }

    @Override
    public Paint createPaint() {
        return new Red();
    }
}
    
TruckFactory.java
        public class TruckFactory implements Factory {

    @Override
    public Vehicle createVehicle() {
        return new Truck();
    }

    @Override
    public Paint createPaint() {
        return new Green();
    }
}
    

The last thing we need is a factory provider, which will provide us a factory for vehicles and their paint. Our example also uses switch expression first introduced in java 12 and available in java 14.

FactoryProvider.java
        public class FactoryProvider {

    public static Factory getFactory(String type) {
        return switch (type) {
            case "CAR" -> new CarFactory();
            case "TRUCK" -> new TruckFactory();
            default -> throw new IllegalArgumentException("Factory of type " + type + " was not recognized");
        };
    }
}
    

Thanks to our provider we get our desired factory that can produce a vehicle and a paint for that vehicle without a user to know the creation logic of those objects. Creation of objects of concrete type is solely handled by factories. Next sample shows usage of this pattern.

App.java
        public class App {

    public static void main(String[] args) {
        Factory carFactory = FactoryProvider.getFactory("CAR");
        Vehicle car = carFactory.createVehicle();
        Paint carPaint = carFactory.createPaint();

        // Prints: Created a vehicle of type: Car and paint: Red
        System.out.println("Created a vehicle of type: " + car.getName() + " and paint: " + carPaint.getName());

        Factory truckFactory = FactoryProvider.getFactory("TRUCK");
        Vehicle truck = truckFactory.createVehicle();
        Paint truckPaint = truckFactory.createPaint();

        // Prints: Created a vehicle of type: Truck and paint: Green
        System.out.println("Created a vehicle of type: " + truck.getName() + " and paint: " + truckPaint.getName());
    }
}
    

Conclusion

That's it! We created and implemented an example of abstract factory pattern. Simple and easy.