Java Top

I just announced the new Learn Spring course, focused on the fundamentals of Spring 5 and Spring Boot 2:

>> CHECK OUT THE COURSE

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:

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.

Java bottom

I just announced the new Learn Spring course, focused on the fundamentals of Spring 5 and Spring Boot 2:

>> CHECK OUT THE COURSE
newest oldest most voted
Notify of
Otávio Augusto Siste
Guest
Otávio Augusto Siste

The class AnimalFactory showed in this article violates the Interface Segregation Principle where clients should not be forced to depend upon interfaces that they do not use. To fix it I would recommend the following change:

public interface AbstractFactory {
T create(String type) ;
}

public class AnimalFactory implements AbstractFactory {
 
    @Override
    public Animal create(String type) {
        if (“Dog”.equalsIgnoreCase(animalType)) {
            return new Dog();
        } else if (“Duck”.equalsIgnoreCase(animalType)) {
            return new Duck();
        }
        return null;
    }
 
}

Loredana Crusoveanu
Editor

Hello,

Thanks for the comment. I’ve updated the article.

Cheers.

java@insset
Guest

IMHO, this is the Factory Method pattern. I think Abstract Factory usually relies on a two steps use :
AnimalFactory factory = xxx.getFactory(animalType);
Animal animal = factory.getAnimal();

The getFactoryMethod can be a static method, an instance method; it returns an implementation of AnimalFactory (the implementation is a “concrete” factory).
An easy and flexible way to implement this method uses a Map.
The getFactory method is then :
public AnimalFactory getFactory(String choice) {
return map.get(choice);
}

As a consequence, adding a new type of Animal does not require to modify anything, just create the Animal and AnimalFactory implementations, register the factory into the Map.

Best regards

java@insset
Guest

Of course, there are other implementations of the getFactory method, for example Toolkit.getToolkit() which is a singleton. The DocumentBuilderFactoy.newInstance() has another behavior too.

Loredana Crusoveanu
Editor

Hello,

Thanks for the comment. I’ve updated the AbstractFactory implementation based on a comment above, and now the sub-classes don’t need to be modified when adding a new type of Animal. The getFactory method in your example is placed in the FactoryProvider class, but it’s using the if statement and not a Map.

Comments are closed on this article!