Java Readability – Single Method with Many Parameters vs. Many Methods Called in Order

javareadability

I have some raw data I need to do many things to (shift it, rotate it, scale it along certain axis, rotate it to a final position) and I am not sure what the best way to do this to maintain code readability. On one hand, I can make a single method with many parameters (10+) to do what I need, but this is a code reading nightmare. On the other hand, I could make multiple methods with 1-3 parameters each, but these methods would need to be called in a very specific order to get the correct result. I have read that it is best for methods to do one thing and do it well, but it seems like having many methods that need to be called in order opens up code for hard-to-find bugs.

Is there a programming paradigm I could use that would minimize bugs and make code easier to read?

Best Answer

Beware of temporal coupling. However, this is not always an issue.

If you must perform steps in order, it follows that step 1 produces some object required for step 2 (e.g. a file stream or other data structure). This alone requires that the second function must be called after the first, it is not even possible to call them in the wrong order accidentally.

By splitting your functionality up into bite-sized pieces, each part is easier to understand, and definitely easier to test in isolation. If you have a huge 100 line function and something in the middle breaks, how does your failed test tell you what is wrong? If one of your five line methods breaks, your failed unit test directs you immediately toward the one piece of code that needs attention.

This is how complex code should look:

public List<Widget> process(File file) throws IOException {
  try (BufferedReader in = new BufferedReader(new FileReader(file))) {
    List<Widget> widgets = new LinkedList<>();
    String line;
    while ((line = in.readLine()) != null) {
      if (isApplicable(line)) { // Filter blank lines, comments, etc.
        Ore o = preprocess(line);
        Ingot i = smelt(o);
        Alloy a = combine(i, new Nonmetal('C'));
        Widget w = smith(a);
        widgets.add(w);
      }
    }
    return widgets;
  }
}

At any point during the process of converting raw data into a finished widget, each function returns something required by the next step in the process. One cannot form an alloy from slag, one must smelt (purify) it first. One may not create a widget without the proper allow (e.g. steel) as input.

The specific details of each step are contained in individual functions that can be tested: rather than unit testing the entire process of mining rocks and creating widgets, test each specific step. Now you have an easy way of ensuring that if your "create widget" process fails, you can narrow down the specific reason.

Aside from the benefits of testing and proving correctness, writing code this way is far easier to read. Nobody can understand a huge parameter list. Break it down into small pieces, and show what each little piece means: that is grokkable.