Abstract factory

The GoF abstract factory pattern aims to separate the creation of objects from their usage. Creating complex objects may involve knowledge about certain prerequisites, implementation details, or which implementation class to be used. Factories help us creating these objects without deep knowledge about the internals. Later in this chapter, we will talk about Domain-Driven Design factories, which are closely related to this pattern. The motivations are the same. Abstract factories aim toward having several implementations of an abstract type where the factory itself is also an abstract type. The users of the functionality develop against interfaces, whereas the concrete factory will produce and return the concrete instances.

There may be an abstract GermanCarFactory with concrete implementations as BMWFactory and PorscheFactory. Both car factories may produce some implementation of GermanCar, be it a BMWCar or PorscheCar, respectively. The client that just wants to have some German car won't care about which actual implementation class the factory will use.

In the Java EE world, we already have a powerful functionality that is in fact a factory framework, namely CDI. CDI provides tons of features to create and inject instances of certain types. Whereas the motivations and outcome are the same, the implementation differs in detail. In fact, there are many ways to realize abstract factories, depending on the use case. Let's have a look at a few of them.

A managed bean can inject instances that are concrete or abstract and even parameterized types. If we want to have only one instance in our current bean, we directly inject a GermanCar:

@Stateless
public class CarEnthusiast {

    @Inject
    GermanCar car;

    ...
}

Having multiple implementations of the GermanCar type would lead to a dependency resolving exception at this point since the container cannot know which actual car to inject. To resolve this issue, we can introduce qualifiers that explicitly ask for a specific type. We could use the available @Named qualifier with defined string values; however, doing so won't introduce typesafety. CDI gives us the possibility to specify our own typesafe qualifiers that will match our use case:

@BMW
public class BMWCar implements GermanCar {
    ...
}

@Porsche
public class PorscheCar implements GermanCar {
    ...
}

Qualifiers are custom runtime-retention annotations, themselves annotated with @Qualifier and typically @Documented:

import javax.inject.Qualifier;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Qualifier
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface BMW {
}

The qualifiers are specified at the injection point. They qualify the injected type and decouple the injection from the actual type being used:

@Stateless
public class CarEnthusiast {

    @Inject
    @BMW
    GermanCar car;

    ...
}

Obtaining an instance of CarEnthusiast will now create and inject a dependent-scoped BMWCar, since this type matches the injection point.

We could now even define a sub-type of a BMW car, that will be used without changing the injection point. This is realized by specializing the BMWCar type with a different implementation. The ElectricBMWCar type sub-classes BMWCar and specifies the @Specializes annotation:

import javax.enterprise.inject.Specializes;

@Specializes
public class ElectricBMWCar extends BMWCar {
    ...
}

Specialized beans inherit the types and qualifiers of their parent type and will be transparently used instead of the parent type. In this example, injecting a GermanCar with @BMW qualifier will provide you an instance of ElectricBMWCar.

However, to be closer to the design pattern described in the book, we could also define a car factory type used to create several cars as desired:

public interface GermanCarManufacturer {
    GermanCar manufactureCar();
}

This car factory is implemented with different specifics:

@BMW
public class BMWCarManufacturer implements GermanCarManufacturer {

    @Override
    public GermanCar manufactureCar() {
        return new BMWCar();
    }
}

@Porsche
public class PorscheCarManufacturer implements GermanCarManufacturer {

    @Override
    public GermanCar manufactureCar() {
        return new PorscheCar();
    }
}

Doing so, the client would now inject and use a manufacturer directly to create new German cars:

@Stateless
public class CarEnthusiast {

    @Inject
    @BMW
    GermanCarManufacturer carManufacturer;

    // create German cars
}

Injecting types that are explicitly defined and qualified, such as our two German cars, provides a lot of flexibility for implementations.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.135.216.75