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.
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.
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.
factory is injected into the client. All related objects are created by the concrete factory.
of products.
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.