Design Patterns in the 21st Century: The Abstract Factory Pattern

Van Nelle Factory IR, by Vincent van der Pas

This is part two of my talk, Design Patterns in the 21st Century.


This pattern is used everywhere in Java code, especially in more "enterprisey" code bases. It involves an interface and an implementation. The interface looks something like this:

public interface Bakery {
    Pastry bakePastry(Topping topping);
    Cake bakeCake();
}

And the implementation:

public class DanishBakery implements Bakery {
    @Override public Pastry bakePastry(Topping topping) {
        return new DanishPastry(topping);
    }

    @Override public Cake bakeCake() {
        return new Aeblekage(); // mmmm, apple cake...
    }
}

More generally, the Abstract Factory pattern is usually implemented according to this structure.

Abstract Factory pattern UML diagram

In this example, Pastry and Cake are "abstract products", and Bakery is an "abstract factory". Their implementations are the concrete variants.

Now, that's a fairly general example.

In actual fact, most factories only have one "create" method.

@FunctionalInterface
public interface Bakery {
    Pastry bakePastry(Topping topping);
}

Oh look, it's a function.

This denegerate case is pretty common in in the Abstract Factory pattern, as well as many others. While most of them provide for lots of discrete pieces of functionality, and so have lots of methods, we often tend to break them up into single-method types, either for flexibility or because we just don't need more than one thing at a time.

So how would we implement this pastry maker?

public class DanishBakery implements Bakery {
    @Override public Pastry apply(Topping topping) {
        return new DanishPastry(Topping topping);
    }
}

OK, sure, that was easy. It looks the same as the earlier DanishBakery except it can't make cake. Delicious apple cake… what's the point of that?

Well, if you remember, Bakery has a Single Abstract Method. This means it's a Functional Interface.

So what's the functional equivalent to this?

Bakery danishBakery = topping -> new DanishPastry(topping);

Or even:

Bakery danishBakery = DanishPastry::new;

Voila. Our DanishBakery class has gone.

But we can go further.

package java.util.function;
/**
 * Represents a function that
 * accepts one argument and produces a result.
 *
 * @since 1.8
 */
@FunctionalInterface
public interface Function<T, R> {
    /**
     * Applies this function to the given argument.
     */
    R apply(T t);

    ...
}

We can replace the Bakery with Function<Topping, Pastry>; they have the same types.

Function<Topping, Pastry> danishBakery = DanishPastry::new;

In this case, we might want to keep it, as it has a name relevant to our business, but often, Factory-like objects serve no real domain purpose except to help us decouple our code. (UserServiceFactory, anyone?) This is brilliant, but on these occasions, we don't need explicit classes for it—Java 8 has a bunch of interfaces built in, such as Function, Supplier and many more in the java.util.function package, that suit our needs fairly well.

Here's our updated UML diagram:

Updated Abstract Factory pattern UML diagram

Aaaaaah. Much better.


Tomorrow, we'll be looking at the Adapter pattern.


This post was cross-posted to my personal blog.

Blogs relacionados

Get content like this straight to your inbox!

Software es nuestra pasión.

Somos Software Craftspeople. Construimos software bien elaborado para nuestros clientes, ayudamos a los/as desarrolladores/as a mejorar en su oficio a través de la formación, la orientación y la tutoría. Ayudamos a las empresas a mejorar en la distribución de software.

Contacto

3 Sutton Lane, planta 3
Londres, EC1M 5PU

Teléfono: +44 207 4902967

2 Mount Street
Manchester, M2 5WQ

Teléfono: +44 161 302 6795

Carrer de Pallars 99, planta 4, sala 41
Barcelona, 08018

Teléfono: +34 937 82 28 82

Correo electrónico: hello@codurance.es