In this tutorial, we will continue our learning of design patterns and cover the Abstract Factory Design Pattern or Abstract Factory Pattern.It is one of the creational patterns and we can find its uses in almost all the libraries in JDK, Spring framework.We will understand what this pattern is about using a Java application. We will then see the design benefits of this abstract factory pattern.
Abstract Factory Pattern
The Abstract Factory pattern or Abstract Factory Method design pattern is one of the creational design patterns. It is very similar to the Factory design pattern, and it adds another layer of abstraction to the factory. We also referred it to as the factory of factories that works as a super-factory which create other factories. In this design pattern, an interface creates a factory of the related objects without specifying their classes. We can give each of the factories the objects as per the Factory design pattern.
Let’s understand this with a very simple real-life example. We have two categories of Animal, domestic and Wild. Now in domestic animals, we have cats and Dogs whereas, in wild animals, we have Tigers and Lions. Here the Animal is an interface (the super-factory) that return the Domestic and Wild are factories.
1. Abstract Design Pattern In Java
To understand the Abstract Factory Pattern better, let’s take a java example. As mentioned above, the super factory in the Abstract Factory design pattern can be interfaces, abstract classes, or non-final classes. Let’s take an example of the Mobile operating system as Abstract Factory, and, with Android and Apple as the factory, and brands like Sony, iPhone, OnePlus, and Lava as actual concrete implementations.
1.1. Super Factory
We will first see the Abstract Factory and this will have a single abstract method getMobile()
. It will have two factory classes extending it. This is our factory of factories.
/**
* Abstract Factory - Factory of Factories
*/
public abstract class AbstractFactory {
abstract IMobile getMobile(String mobileModel);
}
1.2. Factory Producer
This class will produce the actual factories of Apple and Android. It has a getFactory()
method which takes a boolean input (can be String input as well) to check if the customer needs an apple mobile factory or the android mobile factory.
/**
* Factory Producer
*/
public class MpbileFactoryProducer {
public static AbstractFactory getFactory(boolean isApple) {
if (isApple) {
return new AppleMobileFactory();
} else {
return new AndroidMobileFactory();
}
}
}
2. Factories – Android and Apple
We will have two factory classes, AndroidMobileFactory
and AppleMobileFactory
and they will extend the super factory and provide the actual implementation as per the client’s input on mobile brands.
2.1. AndroidMobileFactory
This factory class produces the concrete classes of mobiles that support Android OS, and its method getMobile()
takes a String input as the name of the mobile brand and subsequently gives the concrete object back to the client program.
/**
* Android Mobile Factory
*/
public class AndroidMobileFactory extends AbstractFactory {
@Override
public IMobile getMobile(String mobileModel) {
if (mobileModel.equalsIgnoreCase("Oneplus")) {
return new OnePlus();
} else if (mobileModel.equalsIgnoreCase("Sony")) {
return new Sony();
} else if (mobileModel.equalsIgnoreCase("Lava")) {
return new Lava();
}
return null;
}
}
2.2. AppleMobileFactory
This factory class is responsible for producing the concrete classes of mobiles that support Apple OS, and its method getMobile()
takes a String input as the name of the mobile brand and subsequently gives the concrete object back to the client program.
/**
* Apple Mobile Factory
*/
public class AppleMobileFactory extends AbstractFactory {
@Override
public IMobile getMobile(String mobileModel) {
if (mobileModel.equalsIgnoreCase("iphone")) {
return new Iphone();
}
return null;
}
}
2.3. Mobile Interface
This interface is the superclass for all the mobile phones and has a single method brandName()
, the implementing classes (e.g. Sony, iPhone, etc) will override this method and provide a brand name to the client program.
/**
* Mobile
*/
public interface IMobile {
void brandName();
}
2.4. Concrete Classes
The last step in our Abstract Factory Design Pattern is the concrete classes. These classes create the real mobile object. Here is how they will look like:
/**
* Iphone Mobile
*/
public class Iphone implements IMobile {
@Override
public void brandName() {
System.out.println("The brand name is Iphone");
}
}
/**
* Sony Mobile
*/
public class Sony implements IMobile {
@Override
public void brandName() {
System.out.println("The brand name is Sony");
}
}
/**
* OnePlus Mobile
*/
public class OnePlus implements IMobile {
@Override
public void brandName() {
System.out.println("The brand name is OnePlus");
}
}
/**
* Nokia Mobile Concrete Class
*/
public class Lava implements IMobile {
@Override
public void brandName() {
System.out.println("The brand name is Lava");
}
}
3. Client Program
Let’s see the sample client program to call the factory class and get the required object as per the input.
/**
* Client Class
*/
public class AbstractFactoryPatternDemo {
public static void main(String[] args) {
AbstractFactory abstractFactory1 = MpbileFactoryProducer.getFactory(false);
IMobile onePlus = abstractFactory1.getMobile("Oneplus");
onePlus.brandName();
IMobile sony = abstractFactory1.getMobile("Sony");
sony.brandName();
IMobile lava = abstractFactory1.getMobile("Lava");
lava.brandName();
AbstractFactory abstractFactory2 = MpbileFactoryProducer.getFactory(true);
IMobile iphone = abstractFactory2.getMobile("iPhone");
iphone.brandName();
}
}
Output
The brand name is OnePlus
The brand name is Sony
The brand name is Lava
The brand name is iPhone
Here is the high level class diagram for the Abstract Factory Pattern.
4. Abstract Factory Advantages
Let’s see some advantages of using this pattern:
- Since the abstract factory (or factory) encapsulates creating objects, you can control the classes of objects that an application creates. Also, the client doesn’t know the concrete implementation and can go via interface or abstract class only.
- Abstract Factory Pattern promotes consistency among families of objects because it ensures that if a family of objects is designed to work together then the application use objects from one family at a time. With this pattern, it is very easy to achieve.
- Although it is difficult, Abstract Factory Pattern allows the application to add a new product family later as well.
- Abstract Factory Pattern avoids the tight coupling between concrete products and client code.
4.1. Disadvantages
Remember, this pattern has lots of advantages. However, adding a new family of objects will not be easy because the Abstract Factory’s super-factory (interface or abstract class) fixes the families of objects that can be created. It will require us to change the Abstract Factory and all the sub-classes.
When to use Abstract Factory Design?
1. When our system is comprising multiple groups of objects, and these groups of objects can be used together.
2. When we need a run-time value to decide the concrete implementation of an object.
3. When the client is not concerned about how the system is creating and composing the objects.
Summary
In this post, we talked about the Abstract Factory Design Pattern or Abstract Factory Pattern. We saw some of the real world example along with what are some advantages of using this pattern. We also saw a Java implementation for this design pattern. You can always check our GitHub repository for the latest source code. Here are some examples from the JDK / Spring Framework using the same design pattern.
- javax.xml.parsers.DocumentBuilderFactory#newInstance()
- DatagramSocketImplFactory.