Abstract Factory Pattern in Java

1. Overview

In this article, we’ll discuss the Abstract Factory design pattern.

The book Design Patterns: Elements of Reusable Object-Oriented Software states that an Abstract Factory “provides an interface for creating families of related or dependent objects without specifying their concrete classes”. In other words, this model allows us to create objects that follow a general pattern.

An example of the Abstract Factory design pattern in the JDK is the newInstance() of javax.xml.parsers.DocumentBuilderFactory class.

2. Abstract Factory Design Pattern Example

In this example, we’ll create two implementations of the Factory Method Design pattern: AnimalFactory and ColorFactory.

After that, we’ll manage access to them using an Abstract Factory AbstractFactory:

image

First, we’ll create a family of Animal class and will, later on, use it in our Abstract Factory.

Here’s the Animal interface:

public interface Animal {
    String getAnimal();
    String makeSound();
}

and a concrete implementation Duck:

public class Duck implements Animal {

    @Override
    public String getAnimal() {
        return "Duck";
    }

    @Override
    public String makeSound() {
        return "Squeks";
    }
}

Furthermore, we can create more concrete implementations of Animal interface (like Dog, Bear, etc.) exactly in this manner.

The Abstract Factory deals with families of dependent objects. With that in mind, we’re going to introduce one more family Color as an interface with a few implementations (White, Brown,…).

We’ll skip the actual code for now, but it can be found here.

Now that we’ve got multiple families ready, we can create an AbstractFactory interface for them:

public interface AbstractFactory<T> {
    T create(String animalType) ;
}

Next, we’ll implement an AnimalFactory using the Factory Method design pattern that we discussed in the previous section:

public class AnimalFactory implements AbstractFactory<Animal> {

    @Override
    public Animal create(String animalType) {
        if ("Dog".equalsIgnoreCase(animalType)) {
            return new Dog();
        } else if ("Duck".equalsIgnoreCase(animalType)) {
            return new Duck();
        }

        return null;
    }

}

Similarly, we can implement a factory for the Color interface using the same design pattern.

When all this is set, we’ll create a FactoryProvider class that will provide us with an implementation of AnimalFactory or ColorFactory depending on the argument that we supply to the getFactory() method:

public class FactoryProvider {
    public static AbstractFactory getFactory(String choice){

        if("Animal".equalsIgnoreCase(choice)){
            return new AnimalFactory();
        }
        else if("Color".equalsIgnoreCase(choice)){
            return new ColorFactory();
        }

        return null;
    }
}

3. When to Use Abstract Factory Pattern:

* The client is independent of how we create and compose the objects in the system
* The system consists of multiple families of objects, and these families are designed to be used together
* We need a run-time value to construct a particular dependency

While the pattern is great when creating predefined objects, adding the new ones might be challenging. To support the new type of objects will require changing the AbstractFactory class and all of its subclasses.

4. Summary

In this article, we learned about the Abstract Factory design pattern.

Finally, as always, the implementation of these examples can be found over on GitHub.

Leave a Reply

Your email address will not be published.