When I develop code, I find it helps to use the following rule of thumb:
What? Signature of the method
How? Body of the method
Why? Signature of the caller
That is, what a method does should be expressed clearly by its name or signature. The details of how this is done should be hidden inside the method’s body. Finally, the reason why the method was called should be given by looking at the caller.
What and how – information hiding
The perhaps most common mistake is to mix up the what and how. Common examples of this includes:
- Throwing a low-level or vendor-specific exception from a high-level class, such as an
SQLExceptionfrom an entity manager or repository.
- Unnecessarily returning a concrete implementation of an interface, e.g.
- Excessive use of getters and setters, allowing the caller to “directly” modify the object, rather than providing methods to perform whatever the clients actually need.
This basically comes down to the principle of information hiding. In the above examples, the implementer has failed to hide implementation details from the caller and as a result it will be harder to change these methods’ implementation without also changing their signatures or callers.
What and why – reusability
Another common, although perhaps less so, problem is mixing what and why. I don’t think neither the method’s name nor body, should say attempt to answer the question why. That is, neither should not contain a reason, purpose, or justification for using it.
An example here is that we typically do not have separate classes named
CompanyList doing the same things with different kinds of data, but instead just a
List<T>. Instead, it is up to each caller what the list is used for. (Unless of course a “person list” is an important concept in your domain, in which case it might be perfectly fine to have its own class.)
Letting a method specify why it should be called needlessly limits the applicability of the method and makes it less reusable. Different callers may use the method for different purposes (within reason) and that is perfectly fine. A method should just perform an action and let the caller worry about the reason for doing so.
A great example of the what and why separation, is the stack trace. Looking at a stack trace should in theory allow one to completely understand what is happening in a system. We know what was supposed to happen by looking at the currently executing method’s name and the exception. By then following the names on the stack all the way back to the root, a good stack trace allows us to understand why each step in the chain was taken. This actually applies to every level of the stack trace.