minizibi
minizibi

Reputation: 671

Abstract Factory Pattern implemented as Interfaces

I'm curious about abstract factory pattern. From Java 8 we have default methods, does it mean that we can replace our abstract classes to interfaces? The one downside which I can see it's a case when we need non-static / final field. We can't do it interface. Could you give me some examples (except this one I listed) when old-fashion factories has more advantages?

Upvotes: 1

Views: 771

Answers (2)

payloc91
payloc91

Reputation: 3809

Yes, and it's a widely used technique.

You'll often stumble upon public interfaces, e.g:

public interface Cache<K, V> {
    public Optional<V> get(K k);
    public V put(K k, V v);
    // ...
}

and hidden ( = package-private or nested private ) implementations.

class SoftCache<K, V> implements Cache<K, V> {
    private final Map<K, SoftReference<V> dataMap;
    // ...
}
class WeakCache<K, V> implements Cache<K, V> {
    private final Map<K, WeakReference<V> dataMap;
    // ...
}

There is no need to be multiple implementations, the pattern is valid and fully applicable even with just one sub-class.


Your classes using your caching system do not care about the implementation details. They only care about the behavior exposed, and this is describable very well through an interface.

You are talking about default and factory methods, but they really differ from each other, and I feel you are mixing things up a little bit.

default methods were mostly added because if you have 20 classes implementing MyInterface and you add a method within your interface, it'd be an extremely painful job to implement a behavior (which is usually the same across all classes) in 20 different places.

I feel Java 8/9+ is heavily going towards this pattern : factory methods within interfaces. Take as example the API Set.of(...), Map.of(...), and more.

Take java.util.Stream<T> as an example.

You usually use it this way (for Objects):

private Stream<Element> stream;

or

private IntStream intStream;

You do not care whether an element you are currently having is a Head element, an OfRef or anything else.

These are hidden details, unreachable from your code.

Yet, the interface Stream<T> does expose the factory method of (among others):

/**
 * Returns a sequential {@code Stream} containing a single element.
 *
 * @param t the single element
 * @param <T> the type of stream elements
 * @return a singleton sequential stream
 */
public static<T> Stream<T> of(T t) {
    return StreamSupport.stream(new Streams.StreamBuilderImpl<>(t), false);
}

There is nothing wrong in having your interface expose factory methods instead of abstract classes, but at the end of the day it depends entirely on you and how you feel more comfortable doing things.

It's also to be noted that java usually uses this pattern:

interface > abstract class > other classes

See also java.util.Collection<E> and its sub-classes.

Upvotes: 0

Drew Stevens
Drew Stevens

Reputation: 392

You technically can, but you shouldn't.

The default implementation on an interface is a tool with a few very specific purposes- primarily, for adding functionality to an interface which very likely has been implemented by clients outside of your control, or for an interface which has been implemented repeatedly, where the default implementation would be onerous to re-implement.

They are not intended as a replacement (or even supplement) to abstract classes, when you are extending from some common parent's behavior.

Now, that said, the abstract factory pattern has little to do with Java's use of the abstract keyword. The abstract factory pattern is about hiding (or abstracting away) the concrete factory implementation a given client is actually using to produce an object. What the factory's factory methods are defined as returning may be a concrete class, an abstract class, or an interface.

So, for example-

Suppose you have some class, GuiPainter. It has a method, #paintWindow.

Under the covers, you've introduced a Window, with OS specific implementations like AppleWindow, AndroidWindow, UbunutuWindow (and etc). Each Window implementation varies a little in how it needs to be constructed.

One approach would be to construct GuiPainter with an AppleWindowFactory, an AndroidWindowFactory, an UbuntuWindowFactory (and etc), along with a means to find the OS and decide which factory to use. However, all GuiPainter really wants is any instance of Window- it has no other OS-specific knowledge.

So, instead, we introduce a WindowFactory, which returns a Window. WindowFactory is a Factory which has that knowledge of discovering the OS and deciding which of the concrete Window factories to use- abstracting that responsibility away from GuiPainter.

Window itself might be a concrete class with a single implementation and just configuration difference based on OS. It might be an abstract class with OS-specific implementations (like AppleWindow, AndroidWindow, etc). It might even be an Interface which is implemented anonymously by the factories. What Window is doesn't change that the client no longer has to worry about OS specific nonsense to get the window it wants.

Does that make sense?

Upvotes: 3

Related Questions