With most methods that are called, procedural or OO, there will be (a) parameter(s) passed in. The parameter itself can be a relatively “simple” type [eg. int] or a more complex reference type [eg. string]. The degrees of simplicity referred to here are related to the degrees of state which a parameter can have.
In the case of an int, the value must be set and the range is within an established, and ususally guarded, boundary. In other words, an int has a set size limit and cannot be null. A string, however, can be null and can be of *mostly* any length [physical memory aside]. There are other examples of the relative complexities of parameter types, but these suffice for the discussion. Which is to say, there are relative degrees of complexities in dealing with various parameter types.
In dealing with the various types, there is a responsibility, somewhere between caller and callee, about ensuring the state of the more complex types. In defensive programming, the responsibility lies more with the callee. Should a callee accept a reference type [eg. pointer, string, object reference] it is the responsibility of the callee to validate the state of the parameter before using it. Consider…
public void foo(string param)
Here, foo must check for:
null == param
param.length > 0
or any other explicit constraints about the contents of param which are not essential to the function. In doing so, not only is foo concerned with doing what it needs to do, it also needs to check what it receives before it can function. On the plus, foo documents it’s assumptions about the state of the parameters it functions on. On the drawback, foo also has the responsibility of dealing with bad parameters, in addition to its designed function. In fact, what does foo do if param is NULL. Throw an exception? Conditionally process the function?
If it throws an exception, why check for the state of the parameter anyway since using the parameter in a null state will generate a more reliable[?] exception. Is a system generated exception more accurate than the programmer’s interpretation of a bad parameter. And which is more liable to remain accurate for the lifetime of a function?
Conditionally processing a function can be ambiguous and overloading functions might be a more explicit way to dealing with “conditional” parameters, depending on your design. Also, simply returning from a function because the parameter is not what is expected can also be a design
issue and not completely within the scope of this discussion. The main issue revolves around why should a callee guard against the state of the parameters it receives?
Wether the callee throws its own exception or lets the system throw, the caller is still responsible for handling the exception- or ignoring it. Either way, the caller has expectations about the state of the system [and the parameters] before and after making the call. Those expectations cannot be met if the caller itself is not aware of the state of the parameters it is passing on. If the caller is aware that the parameter it is about to pass is NULL, it shouldn’t pass the parameter unless it knows that itself and the callee both assume NULL is a possibility.
I maintain callers have the responsibility of passing “good” data [garbage in, garbage out] to any method. This makes for a cleaner design in the methods since they work within reasonable assumptions and their code is not dotted with “unneccessary” checks which should have been proactively made further up the call stack [read: closer to the actual problem]. By burdening the callee with the responsibility of not being “open to abuse” is relegating the problem because then i would ask the question: why is the callee being abused to begin with? Surely the real problem lies somwehere else?
But then again, does it hurt that much to put the extra checks in on the callee, as per good defensive programming techniques suggest? Certainly for security reasons, there are certain methods which simply must check their data because they don’t trust anyone calling them. And that too is only by way of design. So in addition to callers being responsible, there are times when the callee has more of the responsibility. Combining the two views, and after a brief, no less passionate debate about the matter with Eugene…
as soon as there is enough information available to make an intelligent assumption about the data you are working with, you should. Be it caller or callee.