Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java, Polymorphism, Static Typing and the "QueryInterface" pattern

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:

  • is the QueryInterface pattern reasonable?
  • should I just use casting?
  • or is the only right way to handle polymorphism in the API to restrict clients to strictly using the plain methods in question? (e.g. the methods in 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.

like image 278
Jason S Avatar asked Nov 13 '14 14:11

Jason S


2 Answers

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.

like image 145
assylias Avatar answered Nov 13 '22 22:11

assylias


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;
        }
    }
like image 1
Tomasz Gawel Avatar answered Nov 13 '22 23:11

Tomasz Gawel