Premise: In my project I have two generically typed interfaces defining Request and Response respectively. A request is processed to yield a response, hence every response is built based on a request. A Processor interface processes a request to build the corresponding response.
Code: The request and response interfaces are:
interface Request<T1>
and
interface Response<T2>
respectively, where T2 and T1 represent generic request and response types (I am deliberately calling them by different names for clarity).
Now, since T2 is a Request, and T1 is a response, so the above code evolves to:
interface Request<T1 extends Response>
and
interface Response<T2 extends Request>
Note that: Request and Response interfaces do not share any inheritance relationship – what the above code only intends to communicate is: Request is typed with only some other type which is-a Response.
Now, consider the Request interface: since Response is again typed, and the response built out of a request will be tied to the original request type, hence, the above code evolves to:
interface Request<T1 extends Response<? extends Request<T1>>>
and
interface Response<T2 extends Request<? extends Response<T2>>
Now, the Processor interface is defined as:
interface Processor<R1 extends Request<R2>, R2 extends Response<R1>> {
R2 process(R1 request);
}
Concrete classes:
Request implementation:
class ConcreteRequest implements Request<ConcreteResponse> {
ConcreteResponse response;
...`
}
Response implementation:
class ConcreteResponse implements Response<ConcreteRequest> {
ConcreteRequest request;
...
}
Processor implementation:
class ConcreteProcessor implements Processor<ConcreteRequest, ConcreteResponse> {
ConcreteResponse process(ConcreteRequest request) {
...
}
}
Question: Is the above code over-designed? Is there a simplified way to represent a tuple of complementary input-output objects?
Unless I’ve totally misunderstood your question, you don’t – and shouldn’t – use generic for this kind of problem. Using polymorphism and/or composition will be much more appropriate. For example, if you need to integrate a copy of the request in the response (hardly necessary but thinkable) then you can add a reference to a request object in your response class.
Technically, this reference to a
Requestobject could be defined using a type; however, you shouldn’t do that because it will always be aRequestobject (either a base class or a derived subclass) and not some kind of arbitrary class that could change with each instanciation of a response.You use generic when the type of each referenced object is totally different (for example, a
List <String>or aList<Request>: there is no subclassing relationship between aStringand aRequestobject) or when the use of polymorphism will not be sufficient because you are defining one or more new virtual functions in a subclass that are not present in the superclass.Building a
Responseto be based on aRequestbecause aRequestis processed to yield aResponseis definitely not the way to go and your currentProcessorinterface is a testimony to that.