Design patterns: Abstract Factory

This is a creational pattern, as it is used to control class instantiation. The abstract factory pattern is used to provide a client with a set of related or dependant objects. The set of objects created by the factory is determined at run-time according to the selection of concrete factory class.

Provides a level of indirection that abstracts the creation of families of related or dependent objects without directly specifying their concrete classes. The factory object has the responsibility for providing creation services for the entire platform family. Clients never create platform objects directly, they ask the factory to do that for them.

This mechanism makes exchanging product families easy because the specific class of the factory object appears only once in the application – where it is instantiated. The application can wholesale replace the entire family of products simply by instantiating a different concrete instance of the abstract factory.

Elements involved:

  • AbstractFactory: This is an abstract base class for the concrete factory classes that will generate new sets of related objects. A method is included for each type of object that will be instantiated.
  • ConcreteFactory: Inheriting from the AbstractFactory class, the concrete factory classes override the methods that generate the suite of objects required by the client.
  • AbstractProduct: This abstract class is the base class for the types of object that a factory can create. One base type exists for each of the distinct types of product required by the client.
  • ConcreteProduct: Multiple subclasses of the ConcreteProduct classes are defined, each containing specific functionality. Objects of these classes are generated by the abstract factory to populate the client.
  • Client: This class uses the factories to generate a family of related objects. In the UML diagram, the client has two private fields that hold instances of the abstract product classes.

We should use the Abstract Factory design pattern when:

  • the system needs to be independent from the way the products it works with are created.
  • the system is or should be configured to work with multiple families of products.
  • a family of products is designed to work only all together.
  • the creation of a library of products is needed, for which is relevant only the interface, not the implementation, too.

Let´s see some code:

public abstract class AbstractFactory {
    public abstract AbstractProduct createProduct();
}
public abstract class AbstractProduct {
    public abstract void operation1();
    public abstract void operation2();
}
public class ConcreteFactory1 extends AbstractFactory {
    @Override
    public AbstractProduct createProduct() {
        return new ConcreteProduct1();
    }
}
public class ConcreteFactory2 extends AbstractFactory {
    @Override
    public AbstractProduct createProduct() {
        return new Product2();
    }
}
public class ConcreteProduct1 extends AbstractProduct {
    public ConcreteProduct1() {
        System.out.println("Creating product 1...");
    }

    @Override
    public void operation1() {
        System.out.println("Executing ConcreteProduct1::operation1...");
    }

    @Override
    public void operation2() {
        System.out.println("Executing ConcreteProduct1::operation2...");
    }
}
public class ConcreteProduct2 extends AbstractProduct {
    public ConcreteProduct2() {
        System.out.println("Creating product 2...");
    }

    @Override
    public void operation1() {
        System.out.println("Executing Product2::operation1...");
    }

    @Override
    public void operation2() {
        System.out.println("Executing Product2::operation2...");
    }
}
public enum ProductType {
    PRODUCT_1,
    PRODUCT_2
}
public class FactoryMaker {

    public static AbstractFactory getFactory(final ProductType productType) {
        final AbstractFactory abstractFactory;

        switch (productType) {
            case PRODUCT_1:
                abstractFactory = new ConcreteFactory1();
                break;

            case PRODUCT_2:
                abstractFactory = new ConcreteFactory2();
                break;

            default:
                throw new IllegalArgumentException("The product type does not exist.");
        }

        return abstractFactory;
    }
}
public class Client {

    public static void main(String[] args) {
        Arrays.stream(ProductType.values())
            .forEach(productType -> {
                AbstractFactory abstractFactory = FactoryMaker.getFactory(productType);
                AbstractProduct product = abstractFactory.createProduct();

                product.operation1();
                product.operation2();
            });
    }
}

The output after the execution should be something like that:

Creating product 1...
Executing ConcreteProduct1::operation1...
Executing ConcreteProduct1::operation2...
Creating product 2...
Executing Product2::operation1...
Executing Product2::operation2...

You can find the code in my GitHub repository “design-apperns“.

Advertisements
Design patterns: Abstract Factory