Abstract Factory Pattern

Abstract factory pattern is a creational pattern. It provides an interface using which a client can instantiate
a group of related Objects. This tutorial explains the Abstract factory pattern using a Car manufacturing algorithm.

Example
We design a framework that can build cars. We build two types of
cars, a Mazda and a Ferrari. The Car Builder also specifies the type of insurance and maintenance plan for the cars.
We use the abstract factory pattern to initialize the Car Object and related insurance and maintenance Objects
together

Lets look at the Car, Insurance and Maintenance Objects.

public interface Car
{
	public String getName();
	public void setInsurance(Insurance insurance);
	public Insurance getInsurance();
	public void setMaintenance(Maintenance maintenance);
	public Maintenance getMaintenance();
}

public class Ferrari implements Car
{
	private String name;
	private Maintenance maintenance;
	private Insurance insurance;

	public Ferrari(String name)
	{
		this.name = name;
	}

	public String getName()
	{
		return name;
	}

	public void setInsurance(Insurance insurance)
	{
		this.insurance = insurance;
	}

	public void setMaintenance(Maintenance maintenance)
	{
		this.maintenance = maintenance;
	}

	public Insurance getInsurance()
	{
		return insurance;
	}

	public Maintenance getMaintenance()
	{
		return maintenance;
	}

}

public class Mazda implements Car
{

	private String name;
	private Maintenance maintenance;
	private Insurance insurance;

	public Mazda(String name)
	{
		this.name = name;
	}

	public String getName()
	{
		return name;
	}

	public void setInsurance(Insurance insurance)
	{
		this.insurance = insurance;

	}

	public void setMaintenance(Maintenance maintenance)
	{
		this.maintenance = maintenance;

	}

	public Insurance getInsurance()
	{
		return insurance;
	}

	public Maintenance getMaintenance()
	{
		return maintenance;
	}

}

public interface Insurance
{
	String getName();
}

public class PartialInsurance implements Insurance
{
	String name;
	public PartialInsurance(String name)
	{
		this.name = name;
	}
	@Override
	public String getName()
	{
		return name;
	}
}

public class CompleteInsurance implements Insurance
{
	String name;
	public CompleteInsurance(String name)
	{
		this.name = name;
	}
	@Override
	public String getName()
	{
		return name;
	}
}

public interface Maintenance
{
	String getName();
}

public class ComprehensiveMaintenance implements Maintenance
{
	String name;
	public ComprehensiveMaintenance(String name)
	{
		this.name = name;
	}
	@Override
	public String getName()
	{
		// TODO Auto-generated method stub
		return null;
	}
}


public class BasicMaintenance implements Maintenance
{
	public BasicMaintenance(String name)
	{
		this.name = name;
	}
	String name;
	@Override
	public String getName()
	{
		return name;
	}
}

Lets look at how we would typically build cars without the abstract factory pattern

public class CarBuilderClient1
{

	public static void main(String[] args)
	{
		CarBuilderClient1 client = new CarBuilderClient1();
		Car car = client.buildFerrari();
		client.showCar(car);
		Car car2 = client.buildMazda();
		client.showCar(car2);

	}

	private Car buildFerrari()
	{
		Ferrari ferrari = new Ferrari("ferrari");
		BasicMaintenance basicMaintenance = new BasicMaintenance("planA");
		PartialInsurance partialInsurance = new PartialInsurance("plan1");
		ferrari.setInsurance(partialInsurance);
		ferrari.setMaintenance(basicMaintenance);
		return ferrari;
	}

	private Car buildMazda()
	{
		Mazda mazda = new Mazda("mazda");
		ComprehensiveMaintenance comprehensiveMaintenance = new ComprehensiveMaintenance("planB");
		CompleteInsurance completeInsurance = new CompleteInsurance("plan2");
		mazda.setInsurance(completeInsurance);
		mazda.setMaintenance(comprehensiveMaintenance);
		return mazda;
	}

	private void showCar(Car car)
	{
		System.out.println(car.getName() + "," + car.getInsurance() + "," + car.getMaintenance());
	}
}
				

In the above example we created two cars. We created maintenance and insurance objects for those cars and
assigned them to the cars. Lets see the problems with this framework and how the Abstract Factory Pattern can be used
to improve the object creation.

  • The client that uses the Car objects also has to know how to create the maintenance
    and insurance objects. If a particular car changes its insurance plan, the client will have to change. Abstract
    Factory pattern can be used here to encapsulate what changes.
  • There is no way to ensure that a car uses only a particular type of Insurance plan.
    Abstract Factory pattern can be used here to instantiate a family of products
    together
    . i.e. we make sure that when we create a Ferrari, we also create a Basic Maintenance plan and a partial
    Insurance plan.

This the class diagram after changing the classes to use the Abstract Factory Pattern.

Abstract Factory Design Pattern

Lets see the classes

Client

public class Client
{
	CarFactory factory;
	public static void main(String[] args)
	{
		Client client = new Client();
		client.setFactory(new MazdaCarFactory());
		client.buildCar("mazda", "Insurance1", "Maintenance1");
	}
	private void buildCar(String name, String insuranceName, String maintenanceName)
	{
		Car car = factory.getCar(name);
		car.setInsurance(factory.getInsurance(insuranceName));
		car.setMaintenance(factory.getMaintenance(maintenanceName));
	}
	public void setFactory(CarFactory factory)
	{
		this.factory = factory;
	}
}

Factories

public abstract class CarFactory
{
   public abstract Insurance getInsurance(String name);
   public abstract Maintenance getMaintenance(String name);
   public abstract Car getCar(String name);
   
}
				

public class FerrariCarFactory extends CarFactory
{
	@Override
	public Insurance getInsurance(String name)
	{
		return new PartialInsurance(name);
	}
	@Override
	public Maintenance getMaintenance(String name)
	{
		return new BasicMaintenance(name);
	}
	@Override
	public Car getCar(String name)
	{
		return new Ferrari(name);
	}
}
				

public class MazdaCarFactory extends CarFactory
{
	@Override
	public Insurance getInsurance(String name)
	{
		return new CompleteInsurance(name);
	}
	@Override
	public Maintenance getMaintenance(String name)
	{
		return new ComprehensiveMaintenance(name);
	}
	@Override
	public Car getCar(String name)
	{
		return new Mazda(name);
	}
}
				

We have removed the object creation process from the client and put it in a separate factory. The factory
creates the group of related objects and is abstract. It is extended by concrete factories to create the actual
products. The client holds a reference to the abstract factory and can access the products through its interfaces but
not the concrete implementations. The factory can be replaced without modifying the client.

The Client contains a reference to an abstract factory. The concrete
factory is injected into the client. All related objects are created by the concrete factory.

New Concrete Factories can be created or extended to create different class
of products.

The client has no idea which concrete product is created. It just works
with the reference to the Product interface.

What are the shortcomings of the abstract factor pattern?

  • The family of related products have to be created together. if you need a different maintenance plan for
    Ferrari, you need to create a new concrete factory or extend the existing one.
  • The pattern requires creation of more classes, and will create a profusion of classes. For simple systems
    this may not be required.

Except for the couple of situations described above, the abstract factory pattern is widely used and can make
the code more robust and adaptable to changes.

Leave a Comment