An important point of writing code in Object Oriented Programming is Object/Instance creation. Any program requires a considerable amount of object creation. Wouldn't it be nice if we had some standard ways to create objects? Hence, came the creational design patterns. These provide standardized pathways to create instances. Abstract Factory pattern in java is one such design pattern.
Let us now take a closer look at Abstract Factory Pattern. In this blog you will find a 7 step approach to understand and implement this pattern. Feel free to leave a comment below, about what do you think of the post and also do share your views on the pattern if you have used it before.
What is Abstract Factory Pattern?
Abstract factory pattern falls into "Creational design pattern" category. Abstract factory design pattern is used to mange different object types of same family. All the object should belong to same family but they are of different categories.
In other word we can say that we have some objects and they are grouped. These objects have some meanings in group. Two objects from different group can not be mixed.
Solution using abstract design factory
For example we have two groups of utensils Microwave safe and non microwave safe products. If we need microwave safe products we should use microwave safe bowl, plate and cup. We can not mix microwave safe and non microwave safe. When we need to avoid mixing we can use abstract factory pattern by creating factories for each.
[wiki] Creating an object often requires complex processes. The object's creation may lead to a significant duplication of code. The factory method design pattern handles these problems by defining a separate method for creating the objects.
This pattern is useful when we have large number of objects in our program. This pattern classifies them into "Families".
Abstract Factory pattern adds a "Factory" that brings together all these factories. Further, it decides at run time which factory should be invoked. This later creates an object of a specific family. Therefore, this pattern is also known as "A Factory of Factories". Abstract Factory Pattern in java encapsulates a group of factories in the process of object creation .
The essence of this pattern is that, it separates the object creation from the object usage. Apart from that, this pattern also creates families of related objects. This helps to keep related objects together and different groups can not be mixed
When can we use Abstract Factory Pattern?
- The system has multiple types (families) of objects
- As a result, if we need different object or functions they should be from the same group
- The system needs to create or compose objects at run time according to the user input
- The system need different function but they should be in groups
- If a(), b(), c() and p(),q(),r() two groups of functions, if we call a() then we can call either function b() or function c() only.
7 Steps to Implement Abstract Factory Pattern:
- Find out the different object types in the application
- Create Interface and implementing classes for each type
For eg, Utensils : Plate, Bowl, Cup, Plate_MW, Bowl_MW, Cup_MW ( _MW is used for microwave safe products) - Create factory classes to group the classes
- Microwave safe: Plate_MW, Bowl_MW, Cup_MW
- Non Microwave safe: Plate, Bowl, Cup
- Declare Abstract factory interface and declare all required methods from factory
- Implement Abstract factory interface by created families
- Create code which will use Abstract factory to get factory and then call the methods on that factory
- Use the abstract factory in the code instate of objects directly
UML : Basic understanding of the implementation
This is the UML diagram of the example discussed above for microwave safe and non microwave safe products.This diagram explains the basic blocks in abstract factory design pattern. Further in the blog, we will use the java code implementation of this example.
Abstract Factory Pattern Java Implementation
Now, let us take a look at how to implement Abstract Factory Pattern in Java.
To begin with, consider a use case where we have a client application that produces different types of utensils. There are 2 major categories(factories) of utensils "Microwave safe" or "Non-Microwave safe". And the factories can create different types of utensils for eg: Plates, Bowls etc.
We will need Interface for parent type as Utensil
public interface Utensil{
public String getType();
public Double getPrice();
public Double getSize();
}
Now, we will create some classes for utensils Plate, Bowl, Cup, Plate_MW, Bowl_MW, Cup_MW ( _MW is used for microwave safe products)
public class Plate implements Utensil{
String type;
.... Other variables....
public Plate() {
this.type = "PATE";
}
... code for other method implementations....
}
public class Bowl implements Utensil{
... code with method implementations
}
public class Bowl_MW implements Utensil{
... code with method implementations
}
public class Cup_MW implements Utensil{
... code with method implementations
}
We have two different type of utensils microwave safe and non microwave safe, as a result, we create factory classes
public Utensil getPlate() {
return new Plate();
}
public Utensil getBowl() {
return new Bowl();
}
public Utensil getCup() {
return new Cup();
}
}public Utensil getPlate() {
return new Plate_MW();
}
public Utensil getBowl() {
return new Bowl_MW();
}
public Utensil getCup() {
return new Cup_MW();
}
}Further, we will create our AbstractUtensilFactory interface. It groups the other factories together.
public interface AbstractUtensilFactory {
public Utensil getPlate();
public Utensil getBowl();
public Utensil getCup();
}
Now we will implement the AbstractUtensilFactory interface by the factories created earlier.
public class MicrowaveSafeFactory implements AbstractUtensilFactory{
..... ..... .....
}
public class NonMicrowaveSafeFactory implements AbstractUtensilFactory{
..... ..... .....
}
Finally, all code is in place and we need to use it. Because, we have two factories, we need to get the appropriate factory first. Once we get the factory we can call the require methods from the factory . Let's create a class FactoryProducer which will provide the factory instance as per the requirement.
There are different ways to implement the classes. there are also optimized ways. we have created as simple as possible for understanding purpose
public class FactoryProducer {
public static AbstractUtensilFactory getFactory(String choice){
if("Microwave".equalsIgnoreCase(choice)){
return new MicrowaveSafeFactory();
}
else if("Non-Microwave".equalsIgnoreCase(choice)){
return new NonMicrowaveSafeFactory();
}
return null;
}
}
Now, we can start using the produces and factory classes where ever we need.
public class Application {
public static void main(String[] args) {
AbstractUtensilFactory utensilFactory = FactoryProducer.getFactory("Microwave");
Utensil utensil = utensilFactory.getPlate();
utensil.getPrice();
}
}
Eventually, in the main method you can see that if we use utensilFactory to get plate, bowl or cup we will get microwave safe utensils only.
Benefits of using abstract factory pattern
- Firstly, it helps to group related objects or functions
- Also, reduces errors of mixing of objects or functions from different groups
- Helps to abstract code so that user don't need to worry about object creations
Limitations
- Only useful when we have to group processes or objects
- Before getting object or calling the function we need to get the factory which adds one more processes
- Adds more classes and abstraction hence code could become complex
Fast track reading
- Most important point, it is a type of “Creational design pattern”
- Mange different object types of same family
- Also known as "Factory of factories"
- It is different from the Factory pattern because it has multiple functions