2

Code design question for Java or Java-like languages (excluding languages which support default values for method parameters such as Javascript flavors):

You would like to implement a method with the following features:

  • Take N parameters, for some reasonably large but finite value of N (like N > 2).
  • O(N) of those parameters (as in, the vast majority of those parameters) are optional.
    • In this case, "optional" means that the method should not error out if the parameters in question are not present. Altering the functionality of the process in some way may be possible. As an example, let's say our method takes (first, middle, last) of a name and prints the name. In this case, we can model the middle name as an "optional" parameter by directly not printing the middle name if it's not present.
  • I don't want a solution that uses a List nor a VarArgs without a good explanation of why it's better.

I want to optimize the answer to this question for scalability; as the number of optional parameters increases, I would prefer if the code for my class remained clean and maintainable.

  • This is exactly what varargs is for, so I don't really understand what your objection is. – Roger Nov 7 '19 at 21:37
  • 1
    VarArgs assumes a) the arguments provided fill a similar purpose and thus can fall under the same name (for code readability) and b) the number of arguments is unknown at build time. In my use case, neither of those things are true; the arguments may share a datatype but may have wildly different functionalities, and the number of arguments is known at build time. – Ertai87 Nov 7 '19 at 22:13
  • but c) I would like to optimize the answer to this question for scalability; as the number of optional parameters increases so a) and b) are only true "eventually". The thing is that too many parameters might come to say that your function has too many reasons to change. Doesn't matter if your code is readable if it smells to tight coupling and low cohesion and 0 encapsulation – Laiv Nov 8 '19 at 7:45
4

If you have few parameters or very few sensible combinations of those parameters go for multiple overloads of the method with different parameter sets. If this generates a substantial number (more than 3-4), go for a config object instead

Why? The number of necessary overloads increases with quadratically with the number of parameters, therefore already even a reasonable set of parameters (like 5) will end up with tons of overloaded methods

| improve this answer | |
  • It's worth pointing out that sometimes an optional parameter only makes sense in the context of another optional parameter . In this case overloading becomes a much better option, as it makes it clear under what circumstances the parameter should be used, and the quadratic increase in overloads is lessened. – Turksarama Nov 7 '19 at 22:34
6

The general answers would be to wrap the parameters either in a class/struct (depending on language) or a list (over which varargs is syntactic sugar). The reason is that when looking at a signature for the method/function, large numbers of parameters become harder and harder to follow.

Note that the composite data type doesn't really fix this, it just hides the problem a little bit. Generally speaking, methods/functions with large numbers of parameters are a design smell, a sign that it might be time to tweak the approach being used.

| improve this answer | |
4

The best practice for many optional parameters is to not create methods that take many parameters. Nerds call this arity.

Now I like a good VarArgs as much as they next code monkey. But remember we're creating code for mere humans to read. VarArgs doesn't solve your problem if only some of them are optional or you want to know which is which.

You can reach for a parameter object to do the heavy lifting by stuffing the parameters that hang out together into one bag.

What you really need is a language with named arguments. Sadly you're using Java, not C# or Python, so you don't have them.

However, you can simulate them using something called the Joshua Bloch Builder.

NutritionFacts cocaCola = new NutritionFacts
    .Builder(240, 8)
    .calories(100)
    .sodium(35)
    .carbohydrate(27)
    .build()
;

Here only the 240 and the 8 were required. The other arguments were optional.

That's more of a constructor thing then a method thing but that can be how you build your parameter object.

Now if you don't care about your parameter object being immutable you can just throw getters and setters at the problem:

NutritionFacts cocaCola = new NutritionFacts(240, 8);
cocaCola.setCalories(100);
cocaCola.setSodium(35);
cocaCola.setCarbohydrate(27); 

The ceremonial code behind this is slightly less nightmarish than the Joshua Bloch Builder but not by much.

printLabel(cocaCola);

Wish Java would get over itself and just add named arguments already. They make optional arguments so simple.

Another best practice with optional args is to use good default values. Not setting an optional argument doesn't mean it has to be null. Even "" can be better.

| improve this answer | |
  • I did think of that approach and it would be the best one, if it wasn't, as you said, specifically a Constructor-based thing. – Ertai87 Nov 7 '19 at 22:14
  • @Ertai87 better? – candied_orange Nov 7 '19 at 22:33
1

If you are in a language with static typing (as Java does), then I would create a helper class for the options, and pass in one of those objects. In your code you would create the options object, set the parameters that you want, and pass it in.

This is somewhat heavyweight, but such data classes are not hard to make, and now type-checking will catch mistakes in parameter names when you compile.

In a dynamic language I would just pass in a hash/dictionary/whatever you want to call it with the parameters that I want sent. Inside of the function I would delete parameters as you go - any unexpected parameters should throw a run-time error. That is less code, clearer, and type-checking wouldn't have helped you anyways.

| improve this answer | |
  • As clarification, how would you represent a non-present optional parameter in such a helper class? – Ertai87 Nov 7 '19 at 22:11
  • @Ertai87 It would be method and property specific. Set them to a default. Have a helper property that records whether it was set. Have a default that means "not actually set". – btilly Nov 7 '19 at 23:57
1

Takes N parameters, for some reasonably large but finite value of N (like N > 2)

In general, you should not.

More than two parameters hints that your method is doing more than one thing, making it harder to use, harder to test, harder to reason about.

In this case, "optional" means that the method should not error out if the parameters in question are not present, although the functionality of the method may be altered in some way.

In general, you should not.

If the behavior is altered, you have two methods rather than one. Except it's not clear that you have two functions, and the behavior when the parameter is specified is implied. That makes the code harder to use, harder to test, harder to reason about.

I would like to optimize the answer to this question for scalability; as the number of optional parameters increases, I would prefer if the code for my class remained clean and maintainable.

Which is why they should not be optional parameters. Anything that grows like this will grow, and in pretty much every org nobody will take time to refactor it when it gets "too big".

Many of my fellow answers suggest horrific class builders or parameter structs. In my experience, that's just rearranging the deck chairs on the Titanic. As you add optional bits, the class still grows and your method still is doing too much work. You should take this as the smell it is and rework your design so that you have simpler functions that operate on around two inputs. Those get composed into slightly bigger scoped methods that still have a single responsibility with a clear, simple behavior. Or decomposed into multiple smaller methods that have a single responsibility with a clear, simple behavior.

Optional parameters have their uses. Parameter structures can be useful. But they're secondary options when you run into this code smell.

| improve this answer | |
0

The simple answer is that you use an object for your parameters. This is a common pattern in Javascript to emulate kwargs-style parameters e.g.:

foo({a: "yes", b: "no"});

For java, it's not a straight forward. A Map would work great here if there was only concise syntax for creating one. You can also define a class. This makes sense if this is a widely used API. If you have very complex requirements around this, you can Josh Bloch's Builder pattern to define this parameter class. This is pretty involved and time consuming so I would only recommend this if it really adds value.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.