Abstract Factory Pattern Explained

Abstract factory pattern is yet another creational design pattern and is considered as another layer of abstraction over factory pattern. In this tutorial, we will expand the scope of car factory problem discussed in factory pattern. We will learn when to use factory pattern by expanding scope of car factory and then how abstract factory pattern solves the expanded scope.

Table of Contents

1. Design global car factory using abstract factory pattern
2. Abstract factory pattern based solution
3. Abstract factory pattern implementation
4. Summary

1. Design global car factory using abstract factory pattern

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

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

To describable the abstract factory pattern, we will consider 3 kind of makes – the USA, Asia, and the default for all other countries. Supporting multiple locations will need critical design changes.

First of all, 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 is being used, so we should be able to use appropriate car factory without even knowing which car factory implementation will be used internally. This also saves us from someone calling the wrong factory for a particular location.

So basically, we need another layer of abstraction which will identify the location and internally use correct car factory implementation without even giving a single hint to the user. This is exactly the problem, which abstract factory pattern is used to solve.

2. Abstract factory pattern based solution

2.1. Package Diagram

Class diagram for participating classes in design of global car factory using 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 have designed the solution to completely hide the location detail with end user. So, I have not exposed any location specific factory directly.

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

3. Abstract factory pattern implementation

Java classes implementing abstract factory pattern for global car factory.

First, wee have to write all separate car factories for different locations. To support, location specific features, begin with 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. Same follows for small and sedan 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 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 3 different Car factories. Now, 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;
  }
}

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

4. Summary

We already have seen the use case scenarios of 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 only difference between factory pattern vs abstract factory pattern.

You can already look deeper into different real time examples of abstract factory 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 got till now.

Happy Learning!!

References:

Abstract factory – Wikipedia

Comments

Subscribe
Notify of
guest
16 Comments
Most Voted
Newest Oldest
Inline Feedbacks
View all comments

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.

Our Blogs

REST API Tutorial

Dark Mode

Dark Mode