I have an interface which is implemented by different classes. I want to make the implementation classes package private so that clients only use the interface, but of course they somehow need to create new instances. I was reading about factory patterns and at first thought Abstract Factory could be what I need, but I'm struggling with the different constructor parameters of the implementation classes.
Example:
public interface Foo {
void doSomething();
}
class FooImplA implements Foo {
String prop1;
Integer prop2;
public FooImplA(String prop1,
Integer prop2) {
this.prop1 = prop1;
this.prop2 = prop2;
}
@Override
public void doSomething() {
}
}
class FooImplB implements Foo {
String prop1;
public FooImplB(String prop1) {
this.prop1 = prop1;
}
@Override
public void doSomething() {
}
}
My solution now would be something like this:
public class FooFactory {
public Foo createFooA(String prop1, Integer prop2) {
return new FooImplA(prop1, prop2);
}
public Foo createFooB(String prop1) {
return new FooImplB(prop1);
}
}
Is this the "right" way to do it or are there better alternatives?
Your question isn't entirely clear, in that there are 2 completely different situations that can both be plausibly turned into this question. I'll answer both.
Think about java.util.List
. Yes, this project (The java.util
package as a project) ships with its own implementations of this interface, including ArrayList
and LinkedList
. However, that interface isn't designed to be limited: If you want to write your own list implementation in your own project you are free to do so, and List
is designed to allow you to do this. Loads of projects use this: guava has ImmutableList
, the java core libs itself has plenty of list implementations for example in the java.util.concurrent
package, and so on.
if this describes your situation, then the answer is:
No, do not make that factory. Just make your implementations public. Yes, this means someone could write:
FooImplA foo = new FooImplA();
and you don't want them to, but I will free you from such concerns by providing you with this pithy but highly effective zen koan: "You can't stop an idiotfrom writing idiotic code; the universe is vastly smarter than you and will create a better idiot than you can write defenses to stop them".
So, it is a pointless exercise, and this is freeing: There is no need to try. Make clear from the API, documentation, and examples how you're meant to do it. If someone is going to ignore all that and 'do it wrong', then let them.
You'll then be setting the system up precisely the same way j.u.List
is: You can write this today:
ArrayList<String> list = new ArrayList<String>();
It compiles just fine. One could say you should 'program to the interface' but as far as style problems go, this rates very lowly indeed. Mostly this doesn't matter. It's the final nit one would pick in a code review if truly nothing of any substance whatsoever is left to be said.
In that case, you need to add a bunch of new language features to do this right:
Java20 adds the notion of 'sealed interfaces'. A sealed interface (or class) lists every type that is allowed to implement/extend it, and no other types can do it. The aim is for you to list your own implementations and leave it at that. This means you can now enforce that nobody makes custom implementations.
The problem with a 'factory' the way you have it, is that it hurts discoverability. I know about your interface type and I have a need to create an instance of it. Where do I look? Naturally, first I look in that interface, but I find no methods that can make anything. Next, I ask my IDE to show me a list of all types that implement it. My IDE gives me a bunch of private classes hidden deep inside your project that clearly I'm not meant to directly new Impl()
, so now I'm stuck. I need to read docs (the horror. I kid, but only a little - if the average user of your library just intuits what to do based on nothing more than the names of your types and methods, that's far better than a system that confuses the heck out of me until I read the docs). Then maybe I see how to do it.
In modern java you can shove static methods in interfaces. So, do that:
// Foo.java:
public sealed interface Foo permits FooImplA, FooImplB {
void doSomething();
public static Foo newImplA() {
return new FooImplA();
}
public static Foo newImplB() {
return new FooImplB();
}
}
// FooImplA.java:
class FooImplA implements Foo {
// note how this is a package private class!
...
FooImplA() { ... } // with a package private constructor!
}
This solves all problems:
Foo.
, hit CTRL+SPACE or whatever the shortcut is, and voila there they are. The java community is used to static 'constructors' and knows to look for these when confused about how to make instances of some type. For that matter, so are IDEs and will auto-suggest these 'static constructors' too.