I'm working on an API design, and there's something about Java and polymorphism that I didn't think about until now. If I create an API like this:
interface FooFactory
{
public Foo getFoo();
}
interface Foo
{
public void sayFoo();
}
then the only thing that my FooFactory implementation can be relied upon to provide is a Foo implementation. If I decide to provide some enhanced methods, like this:
interface EnhancedFoo extends Foo
{
public void patHeadAndRubBelly();
}
class EnhancedFooImpl implements EnhancedFoo
{
... implementation here ...
}
class EnhancedFooFactoryImpl implements FooFactory
{
@Override public EnhancedFoo getFoo() { return new EnhancedFooImpl(); }
}
and the only way my API clients can use the EnhancedFoo interface is if they obtain a Foo interface and try to cast it as the EnhancedFoo.
I remember the way Microsoft handled COM in the IUnknown interface:
HRESULT QueryInterface(
[in] REFIID riid,
[out] void **ppvObject
);
The idea is that you pass in a GUID for the interface you want, if it succeeds, you get back a pointer that guarantees you can safely cast it to the interface you were looking for.
I could do something similar in a typesafe way with Java:
interface FooFactory
{
public <T extends Foo> T getFoo(Class<T> fooClass);
}
where I provide an implementation on request that either returns an instance of the desired subinterface, or returns null if none is available.
My question, is:
Foo in my example and not any EnhancedFoo methods)Clarification: The factory implementation isn't going to be known by client code. (Let's say it uses dependency injection or some service provider architecture like java's ServiceLoader or NetBeans Lookup.) So as a client, I don't know what's available. The factory might have access to several Foo derivatives and I want the client to be able to request a feature set that it wants, and either it gets that, or it will have to fall back on the base Foo functionality.
I guess the hard part for me is that there is runtime dependency here... the pure static typesafe approach where everything is fixed at compile time means that I can only depend on that basic Foo functionality. That approach is familiar to me, but then I lose out on possible enhanced features. Whereas the more dynamic / opportunistic approach is something that can take advantage of these features, but I'm not sure of the right way to architect a system that uses it.
You could simply declare your factory with generics and leave the rest as is:
static interface FooFactory {
public <T extends Foo> T getFoo();
}
Then thanks to type inference, this will compile:
FooFactory f = new EnhancedFooFactoryImpl();
EnhancedFoo e = f.getFoo();
(this may not work prior to Java 8)
If the FooFactory is not what you expected, the line EnhancedFoo e = f.getFoo(); will throw a ClassCastException.
Why not parametrize Factory interface ?
static interface FooFactory<T extends Foo> {
public T getFoo();
}
then:
class EnhancedFooFactoryImpl implements FooFactory<EnhancedFoo> {
@Override
public EnhancedFoo getFoo() { return new EnhancedFooImpl(); }
}
And instantiation:
FooFactory<EnhancedFoo> f1 = new EnhancedFooFactoryImpl();
EnhancedFoo foo = f1.getFoo();
The enhanced factory of course can be used where base class is expected.
FooFactory<?> f2 = new EnhancedFooFactoryImpl();
Foo foo = f2.getFoo();
EDIT (on reply to Your comment)
If for your design it is better to have parametrized factory method not the factory class then it is a good practice to define it as below:
interface FooFactory {
public <T extends Foo> T getFoo(Class<T> fooClass);
}
This gives you advantage in two ways: 1. You can control which actuall clas sa user wants to create. 2. Having a class object you can instantiate it with reflection.
So in this case you do not have to have to special Factory classes for enhanced foo:
class FooFactoryImpl implements FooFactory {
@Override
public <T extends Foo> T getFoo(Class<T> c) {
try {
return c.newInstance();
} catch (ReflectiveOperationException e) {
return null;
}
}
}
Then usage is like below:
FooFactory ff = new FooFactoryImpl();
EnhancedFoo ef = ff.getFoo(EnhancedFoo.class);
Foo f = ff.getFoo(Foo.class);
If some Foo implementation requires constructor parameters you can always put corresponding if in the foctory method and instantiate the object "manually":
@Override
public <T extends Foo> T getFoo(Class<T> c) {
if(SomeParametrizedFoo.class.equals(c)) {
SomeParamtrizedFoo spf = new SomeParamtrizedFoo("constr arg");
spf.setParam(16136);
return (T) spf;
}
try {
return c.newInstance();
} catch (ReflectiveOperationException e) {
return null;
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With