Coping with Methods with Many Parameters

February 21, 2014

I will put my captain Obvious cape now and write in short how to get rid of methods with many parameters, especially if some of the parameters are optional, or depend on each other somehow (e.g. go in pairs, one excludes another, etc.).

Let’s take a simple RestClient class. You need to be able to pass the target resource, optional HTTP headers, HTTP method, request body, timeouts, etc. All of these, except the URL, are optional. How do you write this class?

The first thing that comes to mind is overloading:

public <T> T request(String url);
public <T> T request(String url, HttpMethod method, Object body);
public <T> T request(String url, HttpMethod method, Object body, Map<String, String> headers);
public <T> T request(String url, HttpMethod method, Object body, Map<String, String> headers, long timeout);

Good? No. Because you may want to call a URL with GET, specifying a timeout, but not specifying body or headers. And whenever a new parameter is to be added (e.g. for authentication), then you should copy all the methods and append the parameter. To put it simply: you cannot and should not have an overloaded method for each possible combination of the parameters.

How should we approach the problem? There are several ways, and the one I prefer is using something like the Builder pattern. The Builder pattern is supposed to be used for constructing objects and to replace constructors taking multiple arguments. But its philosophy can be transferred to the above problem. For example:

public <T> T request(RestCall call);

What is RestCall? It’s a mutable object that you configure, preferably with a fluent interface, in order to make your rest call. All fields have getters so that the RestClient can obtain all the fields in needs. A usage might look like this:

client.request(RestCall.to(url).withMethod(HttpMethod.POST)
    .withBody(requestObject).withTimeout(5000));

It reads like prose, it lets you have only one (or very few) methods, rather than overload for each possible combination, and at the same time allows you to have each possible combination of parameters. The “to(..)” method is a simple factory-method. An example implementation of a withX method looks like this:

public RestCall withBody(Object body) {
    this.body = body; //assign a private field
    return this;
}

You can also have reasonable defaults for each field. In this case, a GET method, a predefined default timeout.

This approach also gives you the option to specify constraints. For example, you shouldn’t allow setting a body for GET requests. So:

public RestCall withBody(Object body) {
    if (method == HttpMethod.GET) {
         throw 
              new IllegalStateException("Body not supported for GET");
    }
    this.body = body; //assign a private field
    return this;
}

And if two parameters go together, for example you want to add headers one by one, then you can have an addHeader(String, String) method.

Generally, the approach is more readable and is easier to extend and maintain.

One Response to “Coping with Methods with Many Parameters”

  1. According to DDD’s paradigm, having such methods is rather a sign for code/design smell and they call for general refactoring/redesign, if possible.

    Usually that’s not the case and “serious” refactoring is left for the distant future, so then your approach comes into play (called, if I am not wrong, “Parameter Object” according to M. Fowler in “Refactoring: Improving the Design of Existing Code”).
    In general it’s the silver bullet for situations like that, although I’ve found a minor drawback using such objects. Some times, when unit testing, you have to stub these objects, and this prolongs the test methods, making them also hard to read to some extent. Nevertheless, it’s better than having bunch of overloading methods with multiple parameters. :)

    As for the builders, in case you use Eclipse, there is a splendid plugin for generation of fluent interface builders: https://code.google.com/p/fluent-builders-generator-eclipse-plugin/ I guess you are aware of it. Perhaps for IntelliJ IDEA and other IDEs there is something similar.

    P.S. Sorry for the incomplete comment above. Submitted it by mistake. Will be gratefull if you remove it (as I can’t do it :) )

Leave a Reply