views:

345

answers:

5
+2  Q: 

Generic Interface

Hi All,

Let's say I wanted to define an interface which represents a call to a remote service. Now, the call to the remote service generally returns something, but might also include input parameters. Suppose that an implementing class will typically only implement one service method. Given the above information, is the following a poor design (it doesn't quite feel right):

public interface IExecutesService<A,B>
{
    public A executeService();
    public A executeService(B inputParameter);
}

Now, let's say that I implement this interface with a class that executes a remote service with an input parameter:

public class ServiceA implements IExecutesService<String,String>
{
  public String executeService()
  {
    //This service call should not be executed by this class
    throw new IllegalStateException("This method should not be called for this class...blabla");
  }

  public String executeService(String inputParameter)
  {
    //execute some service
  }

I have two questions regarding the above:

  1. Is the use of a generic interace (IExecutesService<A,B>) good in the case where you want to provide subclasses which require different input parmaters and return types for the interface methods?
  2. How can I do the above better? I.e. I want to group my service executors under a common interface (IExecutesService); however, an implementing class will typically only implement one of the methods, and the use of an IllegalStateException feels really ugly. Also, the B type parameter in IExecutesService<A,B> will be redundant for an implementing class that calls a service without any input parameters. It also seems overkill creating two separate interfaces for the two different service calls.

I will appreciate your comments on this.

Cheers.

+3  A: 

Here's one suggestion:

public interface Service<T,U> {
    T executeService(U... args);
}

public class MyService implements Service<String, Integer> {
    @Override
    public String executeService(Integer... args) {
        // do stuff
        return null;
    }
}

Because of type erasure any class will only be able to implement one of these. This eliminates the redundant method at least.

It's not an unreasonable interface that you're proposing but I'm not 100% sure of what value it adds either. You might just want to use the standard Callable interface. It doesn't support arguments but that part of the interface has the least value (imho).

cletus
Thanks for not putting "I" in front of your interfaces.
Steve Kuo
Prefixing interfaces with "I" is a real .Net'ism. It doesn't really belong in Java code.
cletus
+1  A: 

If I understand correctly, you want to have one class implement multiple of those interfaces with different input/output parameters? This will not work in Java, because the generics are implemented via erasure.

The problem with the Java generics is that the generics are in fact nothing but compiler magic. At runtime, the classes do not keep any information about the types used for generic stuff (class type parameters, method type parameters, interface type parameters). Therefore, even though you could have overloads of specific methods, you cannot bind those to multiple interface implementations which differ in their generic type parameters only.

In general, I can see why you think that this code has a smell. However, in order to provide you with a better solution, it would be necessary to know a little more about your requirements. Why do you want to use a generic interface in the first place?

Lucero
Just for clarity, it's not completely true that classes do not keep ANY information. You can still inspect bounds of parameters (i.e. how the generic class/method was defined) via reflection. Objects do not however, so you cannot tell how (with what parameters) an object was instantiated.
roe
Hi there. I don't want to have a class implementing multiple of these interfaces. A class will only implement one of these interfaces - the problem was that one class will generally only implement one of the methods in the interface, leaving the other redundant...which seemed ugly. The reason I chose generics in the interface was because subclasses will have differing return and input parameter types depending on the service calls they implement. Does this clear things up a bit? Thanks :)
@roe, the bounds (constraints) on the class are there, yes, but that does not give any information about the type effectively used in a specific instance (object). My feeling is that many people using generics in Java are not aware of this.
Lucero
+3  A: 

Here's another suggestion:

public interface Service<T> {
   T execute();
}

using this simple interface you can pass arguments via constructor in the concrete service classes:

public class FooService implements Service<String> {

    private final String input1;
    private final int input2;

    public FooService(String input1, int input2) {
       this.input1 = input1;
       this.input2 = input2;
    }

    @Override
    public String execute() {
        return String.format("'%s%d'", input1, input2);
    }
}
dfa
Considering the remote nature of the interface, I think you would have two remote calls. This might be costly performance-wise :-(
KLE
why would you take two calls? you ought to know what it is that you are calling/creating (or the service factory should), otherwise, how can you call it?
Chii
+1  A: 

I'd stay with two different interfaces.

You said that 'I want to group my service executors under a common interface... It also seems overkill creating two separate interfaces for the two different service calls... A class will only implement one of these interfaces'

It's not clear what is the reason to have a single interface then. If you want to use it as a marker, you can just exploit annotations instead.

Another point is that there is a possible case that your requirements change and method(s) with another signature appears at the interface. Of course it's possible to use Adapter pattern then but it would be rather strange to see that particular class implements interface with, say, three methods where two of them trow UnsupportedOperationException. It's possible that the forth method appears etc.

denis.zhdanov
+1  A: 

As an answer strictly in line with your question, I support cleytus's proposal.


You could also use a marker interface (with no method), say DistantCall, with several several sub-interfaces that have the precise signatures you want.

  • The general interface would serve to mark all of them, in case you want to write some generic code for all of them.
  • The number of specific interfaces can be reduced by using cleytus's generic signature.

Examples of 'reusable' interfaces:

    public interface DistantCall {
    }

    public interface TUDistantCall<T,U> extends DistantCall {
      T execute(U... us);
    }

    public interface UDistantCall<U> extends DistantCall {
      void execute(U... us);
    }

    public interface TDistantCall<T> extends DistantCall {
      T execute();
    }

    public interface TUVDistantCall<T, U, V> extends DistantCall {
      T execute(U u, V... vs);
    }
    ....


UPDATED in response to OP comment

I wasn't thinking of any instanceof in the calling. I was thinking your calling code knew what it was calling, and you just needed to assemble several distant call in a common interface for some generic code (for example, auditing all distant calls, for performance reasons). In your question, I have seen no mention that the calling code is generic :-(

If so, I suggest you have only one interface, only one signature. Having several would only bring more complexity, for nothing.

However, you need to ask yourself some broader questions :
how you will ensure that caller and callee do communicate correctly?

That could be a follow-up on this question, or a different question...

KLE
I can see how this will work, however, this will most likely result in code that requires checking the actual instance being used to call the appropriate method (for e.g. xxx instanceof yyy)... is this type of programming a code smell? It doesn't seem like very good OOP practice. Can someone shed some light on this? Thanks