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?)
a source to share
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.
a source to share
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.
a source to share
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.
a source to share
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(); }
}
a source to share