Abstract Factory Pattern

Abstract factory is a factory of factories; a factory that groups the individual but related/dependent factories together without specifying their concrete classes.

abstract factory design pattern sequence diagram

Abstract factory pattern is yet another creational design pattern and is considered another layer of abstraction over factory pattern. The abstract factory pattern provides a way to encapsulate a group of individual factories that have a common theme without specifying their concrete classes.

In plain words:

Abstract factory is a factory of factories; a factory that groups the individual but related/dependent factories together without specifying their concrete classes.

In this tutorial, we will expand the scope of the car factory problem discussed in the factory pattern. We will learn when to use abstract factory patterns by broadening the scope of the car factory and then how abstract factory pattern solves the expanded scope.

1. Design global car factory using abstract factory pattern

In the factory pattern, we discussed how to abstract the car-making process for various car model types and their additional logic included in the car-making process. Now, imagine if our car maker decides to go global.

To support global operations, we will need to enhance the system to support different car-making styles in different countries. For example, in some countries, the steering wheel is on the left side, and in some countries, it is on the right side. There can be many more such differences in different parts of cars and their making processes.

To describe the abstract factory pattern, we will consider three kinds of makes—the USA, Asia, and the default for all other countries. Supporting multiple locations will require critical design changes.

First, we need car factories in each location specified in the problem statement, i.e., USACarFactory, AsiaCarFactory, and DefaultCarFactory. Now, our application should be smart enough to identify the location where it is being used, so we should be able to use the appropriate car factory without even knowing which factory implementation will be used internally. This also saves us from someone calling the wrong factory for a particular location.

So, we need another layer of abstraction that will identify the client location and internally use the correct car factory implementation without giving the user a single hint. This is exactly the problem that the abstract factory pattern solves.

2. Abstract factory pattern based solution

2.1. Package Diagram

Class diagram for participating classes in the design of global car factories using the abstract factory pattern.

abstract_fctory_package_diagram-9778485

2.2. Sequence Diagram

This diagram shows the interaction between classes and abstraction behind CarFactory factory class.

abstract_factory_sequence_diagram-9375997

Please note that I designed the solution to completely hide the location details from the end user. So, I have not exposed any location-specific factory directly.

In an alternate solution, we can first get the location-specific factory based on the location argument and then use its buildCar() method on abstract reference to build the actual car instance.

3. Abstract factory pattern implementation

Java classes implementing abstract factory patterns for global car factories.

First, wee have to write all separate car factories for different locations. To support location-specific features, begin by modifying our Car.java class with another attribute – location.

public abstract class Car {
 
  public Car(CarType model, Location location){
    this.model = model;
    this.location = location;
  }
 
  protected abstract void construct();
 
  private CarType model = null;
  private Location location = null;
 
  //getters and setters
 
  @Override
  public String toString() {
    return "Model- "+model + " built in "+location;
  }
}

This adds extra work of creating another enum for storing different locations.

public enum Location {
  DEFAULT, USA, ASIA
}

All car types will also have additional location property. We are writing only for the luxury car. The same follows for small and sedans also.

public class LuxuryCar extends Car {

  public LuxuryCar(Location location) {
    super(CarType.LUXURY, location);
    construct();
  }
 
  @Override
  protected void construct() {
    System.out.println("Building luxury car");
    //add accessories
  }
}

So far, we have created basic classes. Now, let’s have different car factories, which is the core idea behind the abstract factory pattern.

public class AsiaCarFactory {

  public static Car buildCar(CarType model) {

    Car car = null;
    switch (model) {

      case SMALL:
      car = new SmallCar(Location.ASIA);
      break;
 
      case SEDAN:
      car = new SedanCar(Location.ASIA);
      break;
 
      case LUXURY:
      car = new LuxuryCar(Location.ASIA);
      break;
 
      default:
      //throw some exception
      break;
    }
    return car;
  }
}
public class DefaultCarFactory {

  public static Car buildCar(CarType model) {

    Car car = null;

    switch (model) {
      case SMALL:
      car = new SmallCar(Location.DEFAULT);
      break;
 
      case SEDAN:
      car = new SedanCar(Location.DEFAULT);
      break;
 
      case LUXURY:
      car = new LuxuryCar(Location.DEFAULT);
      break;
 
      default:
      //throw some exception
      break;
    }
    return car;
  }
}
public class USACarFactory {

  public static Car buildCar(CarType model) {

    Car car = null;
    switch (model) {
      case SMALL:
      car = new SmallCar(Location.USA);
      break;
 
      case SEDAN:
      car = new SedanCar(Location.USA);
      break;
 
      case LUXURY:
      car = new LuxuryCar(Location.USA);
      break;
 
      default:
      //throw some exception
      break;
    }
  return car;
  }
}

Well, now we have all three different Car factories. We have to abstract the way these factories are accessed.

public class CarFactory {

  private CarFactory() {
    //Prevent instantiation
  }
 
  public static Car buildCar(CarType type) {

    Car car = null;
    Location location = Location.ASIA; //Read location property somewhere from configuration
    //Use location specific car factory

    switch(location) {
      case USA:
        car = USACarFactory.buildCar(type);
        break;
      case ASIA:
        car = AsiaCarFactory.buildCar(type);
        break;
      default:
        car = DefaultCarFactory.buildCar(type);
    }
  return car;
  }
}

4. Demo

We are done with writing code. Now, let’s test the factories and cars.

public class TestFactoryPattern
{
  public static void main(String[] args)
  {
    System.out.println(CarFactory.buildCar(CarType.SMALL));
    System.out.println(CarFactory.buildCar(CarType.SEDAN));
    System.out.println(CarFactory.buildCar(CarType.LUXURY));
  }
}

Program output:

Output: (Default location is Asia)
 
Building small car
Model- SMALL built in ASIA
 
Building sedan car
Model- SEDAN built in ASIA
 
Building luxury car
Model- LUXURY built in ASIA

5. Summary

We have already seen the use case scenarios of the Factory pattern, so whenever you need another level of abstraction over a group of factories, you should consider using the abstract factory pattern. It is probably the only difference between a factory pattern and an abstract factory pattern.

You can already look deeper into different real-life examples of abstract factories in JDK distribution:

There are other similar examples, but the need is to have the feel of the abstract factory design pattern, which you must have achieved until now.

Happy Learning!!

Leave a Comment

  1. As per Abstract Factory pattern , It provides an interface for creating families of related or dependent objects without specifying their concrete class. but here the factory is returning only Car type. I think this a Factory method pattern with multiple concrete factory.
    Reference: https://en.wikipedia.org/wiki/Abstract_factory_pattern

    Could anyone please explain which one is correct implementation and why?

    Reply
  2. Hi Lokesh,

    I just want to understand whether location should be part of “Car”?
    Car has nothing to do with location then why did you put location as part of Car?

    //Bhupesh

    Reply
    • Given that each car instance in this application is going to be strictly location specific, how would you determine the location attribute of a random car instance in runtime of application if it’s not tied to car instance itself.

      Reply
  3. CarFactory method needs to be modified every time for switch case , when a location gets added. How could we make it extensible without modification.

    Reply
  4. Hi Supun, In my view interfaces should be used for adding the behavior to classes only. Here, we are creating instances of Car only in different ways. I think that abstract class make more sense here.
    BUT, there is no such restriction from design pattern side, you are free to implement using interfaces as well if you find it more easy. It’s matter of choice only.

    Reply
  5. Hi Lokesh, Thank you for such nice and simple explanation. I have one question, for example I have two different factories one for fruit, one for Vegetable, question is how can I use abstract factory pattern here.

    Reply
    • Talking about myself, I do not see any value addition in making abstract factory for your particular case. Fruits and Vegetables are two different entities and there are many major differences between them. So making two separate factories; one for each, makes more sense to me.

      Also, factory (or abstract factory) pattern should be applied on objects which you can build in different ways; in other words; there can be different representations of a object which belong a common parent.

      Your question is very good but to me, it’s really doesn’t fit into context.

      Reply
  6. Thanks for the article.Its explained well using the example of car.Now I need a real time scenario where this pattern is used(like factory is used in SessionFactory in hibernate)

    Reply

Leave a Comment

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.