RequestResponse
From APIDesign
API Design pattern that solves the problem of growing parameters and return values from API methods. Beyond its basic purpose, it can help with delivering multiple results and providing the results in an incremental way, one by one. It also helps a lot with ability to cancel a computation in the middle.
Code from Compute.java:
See the whole file.public interface Compute { public void computeData(Request request, Response response); public final class Request { // only getters public, rest hidden only for friend code Request() { } } public final class Response { // only setters public, rest available only for friend code private final Map<String,String> result; /** Allow access only to friend code */ Response(Map<String,String> result) { this.result = result; } public void add(String s) { result.put(s, s); } public void addAll(List<String> all) { for (String s : all) { add(s); } } /** @since 2.0 */ public void add(String s, String description) { result.put(s, description); } } }
Evolution Story
The evolution of the whole pattern is based on the assumption that the fixed point of communication with users of the API is the Compute interface with its one method that has to be implemented. From this interface we have two evolution paths:
- clients of the API can require more methods to call. those can be added to Request class
- the infrastructure may want to give the clients more richer way to specify return values. This means to add new methods into the Response class.
Additional Attributes
This API pattern naturally solves the problem of multiple return values from a method. Java does not support that easily, while with Response class, it is easy to add new result methods into it. Allowing type safe extensibility of return types.
Also this API pattern supports incremental return values, as compute method can work in a loop and add one result by another into the Response class, feeding it by as many results as possible.
Related to this, the API pattern allows the infrastructure, which provides the Response class, to process the results sooner before they are fully computed. The compute method can still be running, incrementally computing its results, and yet, the infrastructure may process the already generated values.
Also, if you happen to have multiple providers, it is possible to feed them with the same Request at once, in different threads and collect their responses on a one by one basis, using those that are delivered faster.
Last, but not least, this API pattern supports easy interruption of long computations. The Response or even the Request can contain boolean isCancelled() method which may allow the provider of the compute method implementation to check this value and exist earlier, before everything is computed. In addition to this, the addResult methods can check this flag themselves, and potentially abort the computation by their own, usually by emitting some checked exception.