Java, polymorphism, static input, and the QueryInterface pattern

I am working on an API design, and there is something about Java and polymorphism that I have not thought about so far. If I create an API as follows:

interface FooFactory { public Foo getFoo(); } interface Foo { public void sayFoo(); } 

then the only thing on which the FooFactory implementation can be implemented for the implementation is the Foo implementation. If I decide to provide some advanced methods, for example:

 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 to get the Foo interface and try to use it as EnhancedFoo .

I remember how Microsoft handled COM in the IUnknown interface:

 HRESULT QueryInterface( [in] REFIID riid, [out] void **ppvObject ); 

The idea is that you pass in the GUID for the interface you need, if it succeeds, you get a pointer that ensures that you can safely apply it to the interface you were looking for.

I could do something like this with Java:

 interface FooFactory { public <T extends Foo> T getFoo(Class<T> fooClass); } 

where I provide an on-demand implementation that returns an instance of the required subinterface or returns null if none of them are available.

My question is:

  • Is the QueryInterface pattern reasonable?
  • Should I just use casting?
  • or is it the only correct way to handle polymorphism in the API to restrict customers to strictly using the simple methods in question? (e.g. methods in Foo in my example, not any EnhancedFoo methods)

Explanation: The factory implementation will not be known by the client code. (Let's say it uses dependency injection or some service provider architecture, such as java ServiceLoader or NetBeans Lookup .) So, as a client, I don't know what is available. factory can have access to several Foo derivatives, and I want the client to be able to request the feature set that he wants, and either he will get it, or he will have to return to the basic Foo functionality,

I believe the hard part for me is that there is a dependency on execution. A pure static approach, when everything is fixed at compile time, means that I can only depend on the underlying Foo function. This approach is familiar to me, but then I lose the opportunity for improved features. While a more dynamic / opportunistic approach is something that can take advantage of these features, but I'm not sure about the correct way to create a system that uses it.

+5
source share
2 answers

You can simply declare your factory using generics and leave the rest as they are:

 static interface FooFactory { public <T extends Foo> T getFoo(); } 

Then, thanks to the type inference, this compiles:

 FooFactory f = new EnhancedFooFactoryImpl(); EnhancedFoo e = f.getFoo(); 

(this may not work until Java 8)

If FooFactory not what you expected, the line EnhancedFoo e = f.getFoo(); will raise a ClassCastException .

+1
source

Why not parameterize the 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(); 

Perhaps the extended Factory can be used where the base class is expected.

 FooFactory<?> f2 = new EnhancedFooFactoryImpl(); Foo foo = f2.getFoo(); 

EDIT (reply to your comment)

If it is better for your design to have a parameterized Factory method other than the Factory class, then it is good practice to define it, as shown below:

 interface FooFactory { public <T extends Foo> T getFoo(Class<T> fooClass); } 

This gives you an advantage in two ways: 1. You can control which actall clas sa user wants to create. 2. Having a class object, you can create it with reflection.

So, in this case, you do not need to have special Factory classes for extended 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 use will look like this:

 FooFactory ff = new FooFactoryImpl(); EnhancedFoo ef = ff.getFoo(EnhancedFoo.class); Foo f = ff.getFoo(Foo.class); 

If constructor parameters are required for any Foo implementation, you can always put the appropriate one if you manually create an instance of the object in the foctory method:

  @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; } } 
+1
source

Source: https://habr.com/ru/post/1206810/


All Articles