Home Parameters and Returns
Minimize your parameters
Your method should accept only exactly as many parameters as needed to function, no more, and no less.
In particular, you should:
- Remove any parameters you've never used
- Avoid passing in information that could be derived from other parameters
- Avoid passing in information that could be derived from existing constants
- Avoid passing in information that is already directly contained within fields (when working with objects)
The reasoning behind minimizing your parameters is it helps to minimize the overall complexity of your method. The more parameters you give a method, the more potential interactions (and therefore potential bugs) there will be between each kind of input you could possibly give the method.
You should adopt a mindset where you carefully consider each and every parameter you need to determine whether or not it's necessary, and not fall into the bad habit of adding parameters essentially at whim.
Note that we deliberately do not enforce any sort of limit on how many parameters a method is allowed. Some methods require none, others require one or two, and even more will require seven or more. Again, our emphasis is on whether or not each parameter is necessary or not, not on how many of them there are.
Early returns
Stylistically, we're indifferent as to whether or not you have only a single return in your method or if you have multiple. The only time we require you to return early from a method is if doing so would benefit efficiency (for example, returning a value from the middle of a loop).
For example, it would be good style to return early in a scenario such as this:
public static String foobar(List<String> myCollection) { for (String s : myCollection) { if (checkSomething(s)) { return s; } // do something else with s } return defaultValue; }
It would be inefficient to store the result in a variable and wait to go through the entire list if we didn't need to.
Method chaining
Do not chain your methods together. If you have a sequence of tasks that are meant to be linear, you should keep them linear. If you need data to flow from one method to another to yet another, you should return that data instead of directly passing it some method.
Note that what method chaining is and isn't can be subtle. You'll probably need to read the expanded explanation for this one.
So, full disclosure. I personally hate the name "method chaining". I don't know if it's because of the name we gave it, or if it's because of how we teach it, but I've seen way too many students who seem to have a misunderstanding of what method chaining is, and actually end up writing code worse then they would have otherwise.
In fairness, this might be because it's rather difficult to explain what method chaining actually is without resorting to loads of diagrams and pictures. Let me start with what method chaining is NOT. The term "method chaining", as we use it in this class, does not refer to code that looks like this:
String bar = foo.method1().method2().method3().method4();
Code which looks like this is perfectly fine. It might be a good idea to split it up into several lines and use variables to help make it more readable, depending on what exactly you're doing, but there's nothing inherently wrong with this sort of code.
Method chaining also does not mean "helper methods calling helper methods". To the contrary, it's very much good style to have helper methods calling other helpers, especially if it helps reduce redundancy or helps make your code more readable.
So, what actually is method chaining? Well, I think it's something which is best
explained visually. Let's say that I have a method named myMethod
, and it does
tasks A, B, and C. It doesn't matter what those tasks are – perhaps they're printlns,
perhaps they're if statements, perhaps it's a return statement. Well, we can
represent that method like so:
We have a single method, and it does four tasks in succession. In code, it would look something like this:
public void myMethod(){ taskA; taskB; taskC; taskD; }
This is good, regular, code. We want to do four things, and we do them one after the other. So far, everything's good.
Now, let's say that for whatever reason, you decide to write your code like this:
public void myMethod(){ taskA; methodB(); } private void methodB() { taskB; methodC(); } private void methodC() { taskC; methodD(); } private void methodD() { taskD; }
Well, if we represent it as a flowchart, we basically get this:
Notice how that we've taken what's essentially a linear task, and arbitrarily made one method call another method call another method instead of taking the first approach.
THIS is what method chaining is – it's not private methods calling other private methods. Rather, it's taking what should be an inherently linear task and stringing it along, making the linearity non-obvious. What your code does is starting to become misaligned compared to the "form" or "shape" of your code.
It also makes your code far less modular. Previously, all four tasks were kept independent from one another, and could be rearranged, moved, and deleted at whim. Now, if you want to perform task A, you're forced to also perform tasks B, C, and D whether you want to or not.
90% of the time, the reason why students end up doing method chaining is because they want to pipe data from one function to another, but are afraid of using return statements. For example, they'll do this:
public void myMethod(){ taskA; methodB(something); } private void methodB(String param) { taskB; methodC(something); } private void methodC(String param) { taskC; methodD(something); } private void methodD(String param) { taskD; }
...when they really ought to be using returns, and do something like this:
public void myMethod(){ taskA; something = methodB(something); something = methodC(something); methodD(something); } private void methodB(String param) { taskB; return something; } private void methodC(String param) { taskC; return something; } private void methodD(String param) { taskD; }
...and linearize it. So really, if you look closely at it, method chaining often stems from a misunderstanding or reluctance to use parameters and returns. However, both students AND TAs seem to instead look at the most obvious issue (methods calling other methods) and think that's the main issue when really it's just a symptom of an underlying problem.
As a result, many students end up picking up the wrong instinct and become too paranoid about their code – they grow to incorrectly assume that method chaining is when methods call other methods or something, and that going too deep is bad. Again, there's nothing wrong with methods calling other methods. Take the following flowchart, for example:
This has method calling method after method – it's as deeply nested, if not more, as the previous flowchart. However, this would NOT constitute method chaining. Because the tasks that we're trying to do are inherently non-linear, it makes sense to have multiple helper methods and nesting. What the code is intending to do aligns well with the shape of the code.
So basically, method chaining is whenever you take an inherently non-linear task, and decide to string it along a series of methods (most likely due to misunderstanding how to use parameters and returns) and force a previously independent series of operations to become tangled and dependent – the net result is that you end up with a program with a poor "shape". It feels unbalanced and unhealthy.
If we think about it visually, healthy code is either flat (like a stick resting peacefully on the ground) or like a tree with lots of leaves and branches.
On the other hand, method chaining is like a dead tree, withered and decaying, about to topple over:
So basically, if your code looks like a forest fire hit it, you'll get deducted points for method chaining.