Interfaces declaring methods with abstracts as parameters

I have a best practice question in the following examples:

interface Request;
interface Service {
  void process(Request request)
}

class MyService implements Service;
class YourService implements Service;

class MyRequest implements Request;
class YourRequest implements Request;

      

But how to ensure that you MyService

always receive MyRequest

and YourService

receive only YourRequest

, and not vice versa? The obvious "if-instance-of-check" answer MyService.process(...)

seems ugly and somehow against the SOLID principles. Maybe there are better ways?

Perhaps generics would be a good solution? (But then, how do you use them in code that needs to work under Java 1.4?)

0


a source to share


5 answers


If the design of the contract is that each Service can handle any request, then your implementation of MyService, which only accepts MyRequest (and aborts if other kinds of requests are passed), is wrong.

If the design of the contract is that service and request subclasses map to each other, for example MyService can (and should) only handle MyRequest, then you would need to change the Service interface. Otherwise, the current interface recorded in the question does not do what it describes. One way to fix is ​​to parameterize the service interface:

interface Service<R> {
   void process(R request);
}

      

then your specific MyService would be

public class MyService implements Service<MyRequest> {
   public void process (MyRequest r) {/*blah*/}
}

      



You can see an example of this in action in the JDK - the Comparator interface does exactly that for the same reason. http://java.sun.com/javase/6/docs/api/java/util/Comparator.html

I can't figure out why you want, but if you still want to constrain the MyRequest hierarchy as a request, then you can change Service<R>

toService<R extends Request>

edit: this obviously doesn't work in 1.4, so to do the same [1] you'll need to use the visitor template. Its uglier, but 1.4 is ugly =)

interface Service {
   void process(Request visitor);
}
interface RequestVisitor {
   void visitMyRequest(MyService service);
   void visitYourRequest(YourService service);
   void visitTheOtherRequest(TheOtherService  service);
}
interface Request extends RequestVisitor { /* and any extra methods required for request*/ }
public class MyService implements Service {
   public process(Request r) {r.visitMyRequest(this);}
   public void doSpecialMyProcessing(MyRequest request) { /* your code using MyRequest*/ }
}
public class YourService implements Service {
   public process(Request r) {r.visitYourRequest(this);}
   public void doSpecialYourProcessing(YourRequest request) { /* your code using YourRequest */ }
}
public class MyRequest implements Request {
   void visitMyRequest(MyService service) {
      service.doSpecialMyProcessing(this);
   }
   void visitYourRequest(YourService service) {
      throw new UnsupportedOperation("Cannot call visitYourRequest in MyRequest!");
   }
   void visitTheOtherRequest(TheOtherService  service) {
      throw new UnsupportedOperation("Cannot call visitTheOtherRequest in MyRequest!");
   }
}
public class YourRequest implements Request {
   void visitMyRequest(MyService service) {
      throw new UnsupportedOperation("Cannot call visitMyRequest in YourRequest !");
   }
   void visitYourRequest(YourService service) {
      service. doSpecialYourProcessing(this);
   }
   void visitTheOtherRequest(TheOtherService  service) {
      throw new UnsupportedOperation("Cannot call visitTheOtherRequest in YourRequest !");
   }
}

      

[1] this is not really the same thing, because now you will need to write a method for each subtype of the request. In 1.4, you would need to do and do instanceof, etc. to achieve what 1.5 can do with generics.

+3


a source


Simply put, you are setting up an interface that you then don't want to stick to, so it's not a perfect design.

I mean, if MyService implements a Service, it should be able to accept any requests. Otherwise, it does not fulfill a certain contract.



I would ask why you have a service interface at all in this instance, and if you need it (for other methods) is it a good fit for a method (request request), if subclasses are not going to read it.

+6


a source


In my opinion, generics are better. Your interfaces pretend that the service can handle any type of request. But in reality, the implementations of each of them seem to be closely related.

+2


a source


Anything that implements a Service must expect it to implement its methods. If MyService and YourService require different method prototypes, then they are different interfaces.

Think about it from a different direction. Without knowing the implementation of the Service interface, any caller should be able to call Service.process (request) with any implementation of the request and wait for a valid response.

0


a source


try another level of indirection:

interface Module {
   Service createService();
   Request createRequest();
}

class MyModule implements Module {
   Service createService() { return new MyService(); } 
   Request createRequest() { return new MyRequest(); }
}

class YourModule implements Module {
   Service createService() { return new YourService(); } 
   Request createRequest() { return new YourRequest(); }
}

      

0


a source







All Articles