The Abstract Factory pattern provides a way for creating families of related objects without specifying the concrete type of the objects.
Abstract Factory pattern defines an object, which contains factory methods, and object creation is delegated to that object.
Abstract Factory is a creational pattern.
Factory Method relies on inheritance (incl. interface implementation). It delegates the object creation to subclasses.
Abstract Factory relies on object composition. It delegates the object creation to another object.
The Factory Method is a method that can be overridden by subclasses. Abstract Factory is an object that has multiple factory methods.
Both patterns encapsulate and hide the object creation logic. The type of the created objects are determined at run-time.
In the next Java example, we are going to instantiate different types of cars. For now a car can be a petrol car or an electric car.
In this simple example, a Car
is composed of a few objects:
Engine
, EnergyStorage
and Gearbox
.
These are interfaces and each of them have two implementations.
One for petrol car, and one for electric car. Those are a lot of objects.
The instantiations of them are delegated to two factory classes according to car type.
The Car
object receives a factory object and initializes itself by the help of it.
The car instance will be a petrol or an electric car depending on the type of the factory.
Let’s start with the car parts. These are very simple interfaces and objects. We just need these objects to instantiate them by the factories.
The first is the Engine
interface with a single method.
package com.programcodex.designpatterns.abstractfactory;
public interface Engine {
public String description();
}
Now come two implementations for the Engine
interface.
The PetrolEngine
class demonstrates a petrol car’s engine.
package com.programcodex.designpatterns.abstractfactory;
public class PetrolEngine implements Engine {
@Override
public String description() {
return "Petrol engine";
}
}
The ElectricMotor
class also implements the Engine
interface, and
it drives an electric car.
package com.programcodex.designpatterns.abstractfactory;
public class ElectricMotor implements Engine {
@Override
public String description() {
return "Electric motor";
}
}
The next car part interface is the EnergyStorage
.
package com.programcodex.designpatterns.abstractfactory;
public interface EnergyStorage {
public String description();
}
Petrol car has fuel tank. The FuelTank
class implements the EnergyStorage
interface.
package com.programcodex.designpatterns.abstractfactory;
public class FuelTank implements EnergyStorage {
@Override
public String description() {
return "Fuel tank";
}
}
Electric car has battery. The Battery
class also implements the EnergyStorage
interface.
package com.programcodex.designpatterns.abstractfactory;
public class Battery implements EnergyStorage {
@Override
public String description() {
return "Battery";
}
}
The last car part interface is the Gearbox
.
At this part, there is a difference between petrol and electric cars.
Electric cars don’t have gearboxes, so this interface is implemented only by one class.
package com.programcodex.designpatterns.abstractfactory;
public interface Gearbox {
public String description();
}
For a petrol car, the Gearbox
interface could be implemented several ways.
The next version is an automatic gearbox.
package com.programcodex.designpatterns.abstractfactory;
public class AutomaticGearbox implements Gearbox {
@Override
public String description() {
return "Automatic";
}
}
We are done with the car parts. Now we need factories to instantiate them.
The CarComponentFactory
interface defines methods for this purpose.
package com.programcodex.designpatterns.abstractfactory;
public interface CarComponentFactory {
public String createCarType();
public Engine createEngine();
public EnergyStorage createEnergyStorage();
public Gearbox createGearbox();
}
The PetrolCarComponentFactory
class implements the CarComponentFactory
interface.
Its methods instantiate patrol car related objects.
package com.programcodex.designpatterns.abstractfactory;
public class PetrolCarComponentFactory implements CarComponentFactory {
@Override
public String createCarType() {
return "Petrol"; // It creates a new String Object.
}
@Override
public Engine createEngine() {
return new PetrolEngine();
}
@Override
public EnergyStorage createEnergyStorage() {
return new FuelTank();
}
@Override
public Gearbox createGearbox() {
return new AutomaticGearbox();
}
}
The ElectricCarComponentFactory
class also implements the CarComponentFactory
interface.
It has the same logic as the previous class, but it instantiates electric car components, and
its createGearbox
method returns with null
because electric cars don’t have gearboxes.
package com.programcodex.designpatterns.abstractfactory;
public class ElectricCarComponentFactory implements CarComponentFactory {
@Override
public String createCarType() {
return "Electric"; // It creates a new String Object.
}
@Override
public Engine createEngine() {
return new ElectricMotor();
}
@Override
public EnergyStorage createEnergyStorage() {
return new Battery();
}
@Override
public Gearbox createGearbox() {
return null;
}
}
Finally, here is the Car
object that uses these car parts and factories.
It has three fields to store car parts and one to describe the car type.
It has one constructor with a CarComponentFactory
parameter.
It gives values to its fields by this factory.
The Car
object doesn’t know or care about the concrete implementations of its fields
or the factory in its constructor.
Those can be petrol or electric car related objects, so this pattern is loosely coupled.
package com.programcodex.designpatterns.abstractfactory;
public class Car {
protected String carType;
protected Engine engine;
protected EnergyStorage energyStore;
protected Gearbox gearbox;
public Car(CarComponentFactory factory) {
carType = factory.createCarType();
engine = factory.createEngine();
energyStore = factory.createEnergyStorage();
gearbox = factory.createGearbox();
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder("\n### ");
sb.append(carType).append(" car ###")
.append("\nEngine: ").append(engine.description())
.append("\nEnergy storage: ").append(energyStore.description());
if (gearbox != null) {
sb.append("\nGearbox: ").append(gearbox.description());
}
return sb.toString();
}
}
And we are done. It’s time to run these things.
In the main
method, two factories are created.
One for petrol car, and one for electric car.
After that, two Car
instances are created by them and printed out.
package com.programcodex.designpatterns.abstractfactory;
public class TestAbstractFactory {
public static void main(String[] args) {
CarComponentFactory petrolCarComponentFactory = new PetrolCarComponentFactory();
CarComponentFactory electricCarComponentFactory = new ElectricCarComponentFactory();
Car car = new Car(petrolCarComponentFactory);
System.out.println(car);
car = new Car(electricCarComponentFactory);
System.out.println(car);
}
}
The output of the above test class: