Abstract Factory Design Pattern

Abstract Factory Design Pattern is also called a Super factory or factory of factories. The abstract factory pattern is one level of abstraction higher than the Simple Factory Pattern. We can use this pattern to return one of the several related classes of objects, each of which can return several different objects on request. In another word, an Abstract factory is a factory object that returns one of several groups of classes.

Using the Simple factory pattern we abstract the object creation process of another class. But using the abstract factory pattern we abstract the objects creation process of the family of classes.

Need of Abstract Factory Design Pattern

Let us understand the need for abstract factory design patterns through an example. We have one interface and four classes. Whenever any student is joined then their information should be stored in excel or database.

DAO.java (I)
ExcelStudentDAO.java
ExcelCourseDAO.java
DBStudentDAO.java
DBCourseDAO.java

To choose between ExcelStudentDAO and ExcelCourseDAO we have one ExcelDAOFactory (implementing simple factory pattern). It contains one createDAO() Java method. If we pass “student” then ExcelDAOFactory returns the object of ExcelStudentDAO class, and if we pass “course” then it returns the ExcelCourseDAO class object.

ExcelDAOFactory
   => createDAO(String type)

Similarly, to choose between DBStudentDAO and DBCourseDAO we have one DBDAOFactory (implementing simple factory pattern). The createDAO() method of DBDAOFactory returns the DBStudentDAO class object if we pass “student”, otherwise, it returns the DBCourseDAO class object if we pass “course”.

DBDAOFactory
   => createDAO(String type)

In the client application, it is possible to insert student information in Excel and the database. But it will create lots of problems.

DAO stDAO = ExcelDAOFactory.createDAO("student");
stDAO.insert(); // insert student info in excel sheet
DAO courseDAO = DBDAOFactory.createDAO("course");
courseDAO.insert(); // insert course info to database table

Technically this is the wrong option. Both information (student and course) should be inserted in the same storage unit either in excel or in the database. 

Problem:- Here client application developer is getting the wrong option that is inserting student details to excel and inserting course details to the database.

See problem code at GitHub:- AbstractFactoryDP – Problem.

Some situations where we can face this problem:- Assume food delivery application is developed similar to the above application. Where you can get one item from restaurant-A and another item from restaurant-B in a single order. Have you ever seen this type of application? In two different orders, it is possible to get food from two different restaurants, but in a single order, we have to buy everything only from one restaurant. Else restaurants will face lots of problems, delivery process may become complex, and therefore food delivery application will not survive for a long time.

We have to remove that option. To remove it, only a simple factory pattern is not sufficient, we need to go for a super factory i.e. abstract factory. 

How to Solve Problem using Abstract Factory Design Pattern

Solution:- To overcome this problem, we need to use an abstract factory design pattern. Where implement Simple Factory pattern on the top of existing two DAOFactories and make client application/user getting group of DAO classes object from DAOFactory given by abstract factory pattern.

The file structure of our application,

AbstractFactoryDP-Solution
 =>src
   => com.kp.dao
      => DAO.java(I)
      => DBCourseDAO.java
      => DBStudentDAO.java
      => ExcelCourseDAO.java
      => ExcelStudentDAO.java
   => com.kp.factory
      => DAOFactory.java(I)
      => DBDAOFactory.java
      => ExcelDAOFactory.java
      => DAOFactoryBuilder.java
   => com.kp.test
      => Client.java

The DAO interface contains the method definition,

// DAO.java
package com.kp.dao;
public interface DAO {
   public void insert();
}

The DBCourseDAO and DBStudentDAO implement the DAO interface. Currently, instead of persistence logic, we are simply writing the println() statement to keep things simple.

// DBCourseDAO.java
package com.kp.dao;
public class DBCourseDAO implements DAO {
   @Override
   public void insert() {
      // Persistence logic
      System.out.println("DBStudentDAO: "+
               "Inserting Course Info to database table");
   }
}
// DBStudentDAO.java
package com.kp.dao;
public class DBStudentDAO implements DAO {
   @Override
   public void insert() {
      // Persistence logic
      System.out.println("DBStudentDAO:"+
           " Inserting Student Info to database table");
   }
}

Similarly, ExcelCourseDAO and ExcelStudentDAO implement the DAO interface.

// ExcelCourseDAO.java
package com.kp.dao;
public class ExcelCourseDAO implements DAO {
   @Override
   public void insert() {
      // Persistence logic
      System.out.println("ExcelStudentDAO: " +
            "Inserting Course Info to Excel Sheet");
   }
}
// ExcelStudentDAO.java
package com.kp.dao;
public class ExcelStudentDAO implements DAO {
   @Override
   public void insert() {
      // Persistence logic
      System.out.println("ExcelStudentDAO: " +
          "Inserting Student Info to Excel Sheet");
   }
}

In the com.kp.factory package, the DAOFactory interface contains a method definition to create a DAO object.

// DAOFactory.java
package com.kp.factory;
import com.kp.dao.DAO;
public interface DAOFactory {
   public DAO createDAO(String type);
}

The DADAOFactory class is designed based on a simple factory design pattern and helps to create DBStudentDAO or DBCourseDAO objects.

// DBDAOFactory.java
package com.kp.factory;
import com.kp.dao.DAO;
import com.kp.dao.DBCourseDAO;
import com.kp.dao.DBStudentDAO;

// simple factory pattern
public class DBDAOFactory implements DAOFactory {
   @Override
   public DAO createDAO(String type) {
      if(type.equalsIgnoreCase("student")) {
         return new DBStudentDAO();
      } else if(type.equalsIgnoreCase("course")) {
         return new DBCourseDAO();
      } else {
         throw new IllegalArgumentException("Invalid type");
      }
   }
}

The ExcelDAOFactory implements DAOFactory and helps to create ExcelStudentDAO or  ExcelCourseDAO objects based on the input value.

// ExcelDAOFactory.java
package com.kp.factory;
import com.kp.dao.DAO;
import com.kp.dao.ExcelCourseDAO;
import com.kp.dao.ExcelStudentDAO;

// simple factory pattern
public class ExcelDAOFactory implements DAOFactory {
   @Override
   public DAO createDAO(String type) {
      if(type.equalsIgnoreCase("student")) {
         return new ExcelStudentDAO();
      } else if(type.equalsIgnoreCase("course")) {
         return new ExcelCourseDAO();
      } else {
         throw new IllegalArgumentException("Invalid type");
      }
   }
}

The most important part of the solution (DAOFactoryBuilder). This class is acting as AbstractFactory implementation and it helps to create a factory on factories or super factories.

// DAOFactoryBuilder.java
package com.kp.factory;

// class acting as AbstractFactory 
// (factory on factories or super factory)
public class DAOFactoryBuilder {

   // abstract factory logic
   // method returning one of the factories object
   public static DAOFactory buildDAOFactory(String storetype) {
      if(storetype.equalsIgnoreCase("db")) {
         return new DBDAOFactory();
      } else if(storetype.equalsIgnoreCase("excel")) {
         return new ExcelDAOFactory();
      } else {
         throw new IllegalArgumentException("Invalid store type");
      }
   }
}

In the client/user class,

// Client.java
package com.kp.test;
import com.kp.dao.DAO;
import com.kp.factory.DAOFactory;
import com.kp.factory.DAOFactoryBuilder;

// Good client
public class Client {
   public static void main(String[] args) {

      // get DAOFactory
      DAOFactory dbDAOFactory = 
           DAOFactoryBuilder.buildDAOFactory("db");
      DAO studentDAO = dbDAOFactory.createDAO("student");
      DAO courseDAO = dbDAOFactory.createDAO("course");
      
      // insert student info to DB
      studentDAO.insert();
      // insert course info to DB
      courseDAO.insert();
   }
}

Output for Client:-

DBStudentDAO: Inserting Student Info to database table
DBStudentDAO: Inserting Course Info to database table

Now, the client application developer is bounded to use either database or excel. They can’t use both of them at a time. See the solution code at GitHub:- AbstractFactoryDP-Solution

The DAOFactoryBuilder will take care of instantiating the appropriate factory to work with the family of DAOs. For any application, it is important to use all DAOs that belong to the same type. So our DAOFactoryBuilder enforces this rule by encouraging you to get one type of factory from which we can use DAOs to perform persistence operations. In the above example, we are using the “db” family to perform operations. If we want to switch from “db” to “excel” then we don’t need to make a lot of modifications as we are dealing with the DAOFacotry abstract class, we can easily switch between any of the implementation of DAOFactory by calling DAOFactoryBuilder.buildDAOFactory(-) method.

Leave a Comment

Your email address will not be published. Required fields are marked *