Home Commenting public vs private methods
Public vs. private comments
Previously, we stated that there are three kinds of methods: class header comments, method header comments, and inline comments.
We are going to extend this model further by taking a look at the differences between public method header comments, and private method header comments. We expect slightly different things from the different types of method headers.
Public methods are mean to be written for a client who only cares how to use your class. Class header comments are like public method comments, except more general. Private method header comments are meant to be read by people who are adding new public methods to your class. Inline comments are meant to be read by people who need to directly modify your class. You should write all of your comments accordingly.
Another way of thinking about these four comments is to think of them as sitting on a sliding scale of how much implementation detail is permitted. Public method and class header comments should contain zero implementation detail, inline comments are usually all about implementation detail, and private method header comments will sit somewhere in between.
However, you should not make the mistake of assuming that we are more lenient regarding private method comments or inline comments. We expect the same degree of quality in rigor in all three types of comments – it's just that the topic of each will be different.
Since we have already discussed what we expect from class header comments and inline comments in the previous portions of this guide/in CSE 142, this section of the guide will focus on the difference between public and private method header comments.
Public comments
The client of a public method is somebody who might want to use your code. As a result, you should avoid including info about how exactly your class and methods work and instead focus on commenting external behavior.
Public comments are comments that are meant to be addressed to the client. The only thing the client cares about is how to use your class, and what they can expect from it. In particular, the client does NOT care about how exactly your class works, or any of its internal details.
Rather, the purpose of a comment is to describe the boundary or interface between two bodies of code. This means that when commenting methods, you should take care to focus on describing preconditions, which are things which must be true if the client wants to successfully use your method, and postconditions, which are things your method guarantees it will do assuming all the preconditions are met.
Here are some examples of things that would constitute as preconditions and postconditions:
Preconditions:
-
Parameters:
What is the expected range of your parameters? Do they need to be in any particular format? What constitutes a valid and invalid parameter?
-
Exceptions:
What happens if the user passes in an invalid parameter? If you throw an exception, what kind of exception do you throw? Under what specific conditions will an exception be thrown?
-
State:
Does the object need to be in any particular state in order for the method call to complete successfully? If so, what is that state? What does the client need to do in order to put the object in that state?
Postconditions:
-
Return:
What will the method return once the method successfully finishes? What is the expected range and format of whatever you return? Is the return value in any particular format? What constitutes a valid and invalid return value?
-
Behavior:
Besides returning some value, is there something else the method will do, like printing something or modifying some state? Be sure to comment only on things the client will actually notice and care about.
You should also take care to explicitly avoid ever discussing implementation detail, which is information about how specifically your method works.
That way, if you avoid elaborating on the internal details of the method, you're free to arbitrarily change how your class works in the future without changing the comments and without disturbing the client. All the client cares about is how they would go about using your class. For example, a client of ArrayIntList doesn't care exactly how ArrayIntList works – all they care about is how to use the class to store ints.
Here some examples of things which would constitute as implementation detail:
-
Fields
The client should be completely ignorant as to what fields you're using, what you're using them for, the names of your fields, etc. If you mention your fields, you're describing internal details for them, which the client won't care about.
-
Specific data structures or algorithms
Again, you want to retain as much freedom as possible to change how your class internally works without bothering the client. You might think that it's best to use an array on Monday, change your mind and use a Queue on Tuesday, and change your mind yet again and use a TreeMap on Wednesday. You want to retain as much flexibility as possible without over-committing.
-
Information about how specifically your method works
Same reasoning as above.
Private comments
The client of a private method is somebody who wants to add a new public method to your class, and will potentially use your private method to do so. As a result, it's ok to talk about some internal details such as your fields, but still not ok to talk about how your method works.
Note that the expectations for private method header comments are nearly identical to those for public method header comments, except that our target audience has shifted. For public method header comments, the clients are people who are interested in using your class in some way. For private method header comments, the clients are people who are interested in adding a new public or private method to your class, and will be using (though not modifying) your private method in some way.
As a consequence, you should still take care to comment on all preconditions and postconditions, and describe all parameters, potential exceptions, expected behavior, and so forth.
However, because the client is somebody who is extending the functionality of your class, the client will naturally be able to view the specific fields and therefore can deduce quite a bit about what algorithms and data-structures your class uses.
Because of that, there's really no point in trying to word your private method header comments in such a way to try and hide information about fields, data-structures, and algorithms from your client. Rather, it's encouraged to talk about those sorts of things since it's potentially useful for your client to understand how exactly your method manipulates and works with your fields.
However, do keep in mind that your comments should NOT talk about how specifically your method works. That still counts as implementation detail, since there's no need for the client to know how exactly your private method works.
Redundancy in comments
While having redundancy in code is very bad and should be avoided whenever possible, redundancy in comments is sometimes unavoidable and is not as big of a deal.
If your method header comments for two methods are very similar, it's acceptable to have some duplicated information. If those two comments are for public methods, it's acceptable to have one comment refer to the other. If you have a public method and a private method which are very similar (for example, for public-private recursion problems), you should adjust your comments for the private method to focus more on the differences between the two methods. More examples follow below.
When you have two public methods which are very similar, you are allowed to let them explicitly mention each other. For example, this would be an example of acceptable commenting:
// Picks a single flower of the given `color` and adds it // to the flower basket. // // Equivalent to calling `pickFlowers(color, 1)` (including all // preconditions, postconditions, and exceptions) public void pickFlower(Color color) { pickFlowers(color, 1); } // Picks `n` flowers of the given `color` and adds it // to the flower basket. // // Preconditions: // - `color` must not be null, otherwise an IllegalArgumentException // will be thrown. // - `n` must be 1 or greater, otherwise an IllegalArgumentException // will be thrown. // - The internal flowerbasket must not be full, otherwise an // IllegalStateException will be thrown. // // Postconditions: // - The flowers are added to the flower basket, assuming it is not // full. public void pickFlowers(Color color, int n) { // ...snip... }
Even though pickFlower
and pickFlowers
have very similar
preconditions and postconditions, the simpler method pickFlower
can simply
state that it's equivalent to calling another method in a certain way and thus avoid
having to repeat a lot of the comments.
However, if you have a public method and a private method which are very similar (for example, when writing a pair of public-private methods for recursion problems), you cannot have the public method refer to the private one. After all, the client of the class is unable to view any of your private method header comments so would not be able to view what you are referring to.
Instead, you should adjust your private method header comment so it focuses more on implementation and the differences between it an the public method.
As an example, let's say we have a method which takes a number, and prints out all the different ways we can start from 0 and add up the numbers 1 or 2 to reach that number.
In that case, this would be an acceptable way of commenting the public and private methods with minimal redundancy:
// Prints out all the ways you can add up the numbers 1 and 2 // in order to reach the provided number `n`. For example, calling // `printPossibleSums(4)` would produce the following output: // // 1 1 1 1 // 1 1 2 // 1 2 1 // 2 1 1 // 2 2 // // Preconditions: // - `n` must be non-negative // // Postconditions: // - prints out all possible combinations separated by space // to the console, with one combination per line. public void printPossibleSums(int n) { printPossibleSumsHelper(n, ""); } // Part of the public-private pair for printPossibleSums. // Recursively explores all the different ways you can // add the numbers 1 or 2 to reach `n`. // // Preconditions: // - `path` must contain a sequence of numbers explored so // far, separated by space. The entire string must end // with a single space. // // Postcondition: // - When `n == 0`, prints out the path to the console. private void printPossibleSums(int n, String path) { if (n == 0) { System.out.println(path); } else if (n > 0) { printPossibleSums(n - 1, path + "1 "); printPossibleSums(n - 2, path + "2 "); } }
Notice how our public pair went into great detail about the high-level details of the method was meant to do, whereas the private pair when into roughly equal detail, but focused more on implementation and the new parameter we just added, instead of focusing on what it did identically to the public pair.
Comment your exceptions
Whenever you have a method that could potentially throw an exception
(IllegalArgumentException
, FileNotFoundException
, etc), you
should mention that your code could throw this exception, and under what conditions.
If the client wants to use your code, it's very important that they understand the limitations of your method. In particular, if it's possible for your method to fail to work under some conditions, you should be very detailed about what those conditions are and what'll happen if they're violated.
For example, take the following code:
// This method returns 'true' if the array contains the value, and // false otherwise. public static boolean contains(int[] numbers, int value) { if (numbers == null) { throw new IllegalArgumentException("array cannot be null"); } for (int i = 0; i < numbers.length; i++) { if (numbers[i] == value) { return true; } } return false; }
This code will fail if numbers
happens to be null. However, our comments
are ignoring this fact completely – the client doesn't know that this is a problem,
will probably attempt passing in null at some point assuming everything will work, and will
be very surprised when it doesn't.
Instead, you should explicitly indicate that this is a problem, like so:
// This method returns 'true' if the array contains the value, and // false otherwise. It will throw an IllegalArgumentException if the // array of numbers is null. public static boolean contains(int[] numbers, int value) { if (numbers == null) { throw new IllegalArgumentException("array cannot be null"); } for (int i = 0; i < numbers.length; i++) { if (numbers[i] == value) { return true; } } return false; }
Note that we not only mention the specific kind of exception, but under what specific conditions it'll fail.
Now, what if the spec did not tell us to include an exception in our method? What if the spec told us to write the following instead? How should we comment it?
// This method returns 'true' if the array contains the value, and // false otherwise. public static boolean contains(int[] numbers, int value) { for (int i = 0; i < numbers.length; i++) { if (numbers[i] == value) { return true; } } return false; }
If we try passing in null now, the code still won't work, so it's important that we comment this. However, although our code happens to throw a NullPointerException in this case, that's more a reflection of the fact that our exceptions are incomplete, not a fundamental part of the spec and contract.
So, in this case, it's ok to not explicitly mention the exception, since our code never explicitly handles an exception:
// This method returns 'true' if the array contains the value, and // false otherwise. The array of numbers must not be null. public static boolean contains(int[] numbers, int value) { for (int i = 0; i < numbers.length; i++) { if (numbers[i] == value) { return true; } } return false; }