4

Let's say I wanted to define an interface which represents a call to a remote service.

Both Services have different request and response

public interface ExecutesService<T,S> {
    public T executeFirstService(S obj);
    public T executeSecondService(S obj);
    public T executeThirdService(S obj);
    public T executeFourthService(S obj);
}

Now, let's see implementation

public class ServiceA implements ExecutesService<Response1,Request1>
{
  public Response1 executeFirstService(Request1 obj)
  {
    //This service call should not be executed by this class
    throw new UnsupportedOperationException("This method should not be called for this class");
  }

  public Response1 executeSecondService(Request1 obj)
  {
    //execute some service
  }

   public Response1 executeThirdService(Request1 obj)
  {
    //execute some service
  }

   public Response1 executeFourthService(Request1 obj)
  {
    //execute some service
  }
}


public class ServiceB implements ExecutesService<Response2,Request2>
{

 public Response1 executeFirstService(Request1 obj)
  {
    //execute some service
  }

  public Response1 executeSecondService(Request1 obj)
  {
      //This service call should not be executed by this class
    throw new UnsupportedOperationException("This method should not be called for this class");
  }

   public Response1 executeThirdService(Request1 obj)
  {
      //This service call should not be executed by this class
    throw new UnsupportedOperationException("This method should not be called for this class");
  }

   public Response1 executeFourthService(Request1 obj)
  {
    //execute some service
  }
}

In a other class depending on some value in request I am creating instance of either ServiceA or ServiceB

I have questions regarding the above:

Is the use of a generic interface ExecutesService<T,S> good in the case where you want to provide subclasses which require different Request and Response.

How can I do the above better?

Vasu
  • 21,832
  • 11
  • 51
  • 67
VedantK
  • 9,728
  • 7
  • 66
  • 71

4 Answers4

1

I guess it is not a good idea to implement interface and make possible to call unsupported methods. It is a sign, that you should split your interface into two or three, depending on concrete situation, in a such way, that each class implements all methods of the implemented interface.

In your case I would split the entire interface into three, using inheritance to avoid doubling. Please, see the example:

public interface ExecutesService<T, S> {
    T executeFourthService(S obj);
}

public interface ExecutesServiceA<T, S> extends ExecutesService {
    T executeSecondService(S obj);
    T executeThirdService(S obj);    
}

public interface ExecutesServiceB<T, S> extends ExecutesService {
    T executeFirstService(S obj);
}

Please, also take into account that it is redundant to place public modifier in interface methods.

Hope this helps.

Vasiliy Vlasov
  • 3,316
  • 3
  • 17
  • 20
1

It makes not really sense to have a interface if you know that for one case, most of methods of the interface are not supported and so should not be called by the client.
Why provide to the client an interface that could be error prone to use ?
I think that you should have two distinct API in your use case, that is, two classes (if interface is not required any longer) or two interfaces.

However, it doesn't mean that the two API cannot share a common interface ancestor if it makes sense for some processing where instances should be interchangeable as they rely on the same operation contract.

Is the use of a generic interace (ExecutesService) good in the case where you want to provide subclasses which require different Request and Response.

It is not classic class deriving but in some case it is desirable as it allows to use a common interface for implementations that has some enough similar methods but don't use the same return type or parameter types in their signature :

public interface ExecutesService<T,S>

It allows to define a contract where the classic deriving cannot.

However, this way of implementing a class doesn't allow necessarily to program by interface as the declared type specifies a particular type :

ExecutesService<String, Integer> myVar = new ExecutesService<>();

cannot be interchanged with :

ExecutesService<Boolean, String> otherVar

like that myVar = otherVar.

I think that your question is a related problem to.
You manipulate implementations that have close enough methods but are not really the same behavior.
So, you finish to mix things from two concepts that have no relation between them.

By using classic inheriting (without generics), you would have probably introduced very fast distinct interfaces.

davidxxx
  • 125,838
  • 23
  • 214
  • 215
1

I would suggest you to create two different interfaces every of which is handling its own request and response types. Of course you can develop an implementation with one generic interface handling all logic but it may make the code more complex and dirty from my point of view. regards

1

Basically, your current design violates open closed principle i.e., what if you wanted to add executeFifthService() method to ServiceA and ServiceB etc.. classes.

It is not a good idea to update all of your Service A, B, etc.. classes, in simple words, classes should be open for extension but closed for modification.

Rather, you can refer the below approach:

ExecutesService interface:

public interface ExecutesService<T,S> {
    public T executeService(S obj);
}

ServiceA Class:

public class ServiceA implements ExecutesService<Response1,Request1> {

    List<Class> supportedListOfServices = new ArrayList<>();
    //load list of classnames supported by ServiceA during startup from properties

    public Response1 executeService(Request1 request1, Service service) {
        if(!list.contains(Service.class)) {
           throw new UnsupportedOperationException("This method should 
                      not be called for this class");
        } else {
            return service.execute(request1);
        }
    }
}

Similarly, you can implement ServiceB as well.

Service interface:

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

FirstService class:

public class FirstService implements Service<Request1,Response1> {
    public Response1 execute(Request1 req);
}

Similarly, you need to implement SecondService, ThirdService, etc.. as well.

So, in this approach, you are basically passing the Service (to be actually called, it could be FirstService or SecondService, etc..) at runtime and ServiceA validates whether it is in supportedListOfServices, if not throws an UnsupportedOperationException.

The important point here is that you don't need to update any of the existing services for adding new functionality (unlike your design where you need to add executeFifthService() in ServiceA, B, etc..), rather you need to add one more class called FifthService and pass it.

Community
  • 1
  • 1
Vasu
  • 21,832
  • 11
  • 51
  • 67