In implementing the NullObject
It seems to me this will be less time-efficient than directly checking for null because it requires an extra indirect method-call for each leaf node. (Not that efficiency is always the highest priority and calls will be often be overwhelmed by other factors.) -- MartinPool
And if for any reason you call several
dummy methods instead of one null check, you obviously execute more code (and I-cache misses can be very expensive)
A good principle of design is to keep the common case fast/simple. If the common case is that the thing being checked in null then it will generally be more efficient to implement the checks (though note exception below).
If the common case is that the thing will not be null then any time, however minimal, spent doing checks is wasted most of the time. Code without checks is likely to be simpler (to read) that that with the checks.
The important fact that makes the null pattern so important is that its cost is essentially *zero*. Thes is because the NullObject
pattern is used in a context where you would be sending a message anyway. To reuse the unix analogy, instead of writing
if ( $out != "/dev/null" ) echo "hello world" > $out
you simply write
echo "hello world" > $out
is not a null pointer, nor a nil reference. It is a full object, which conforms to the interface of the thing for which it acts as a Null. That interface may provide an isNull method. If it does, then call that method will be as expensive as any other. The greater code density of using a null object, and the reduction in the number of branches, may offset any advantage for cache performance. It would only be worth while checking "isNull" if performance analysis indicates that the null case is a common case, and that it is significant in the context of the system.
The "isNull" function would have to be polymorphic (virtual in C++), so it would have a non-trivial cost.
You could avoid this cost by having a SingletonPattern NullObject
and using a static inline function to check to see if any given object is the NullObject
Then, the test would be nearly as efficient as checking directly for the NULL pointer value.
But you'd have to have extraordinarily severe performance constraints
to justify this level of complexity.
Still, even in this case, having a NullObject
instead of using a NULL valued pointer makes it possible to optimize the one place where you want to check for "null" before calling, but to make virtual function calls to the NullObject
in all other places in your code.
So, NullObject is still the winner! ;->
Would the HotSpot
compiler with dynamic inlining turn the message into a simple conditional?
In Smalltalk, message sends are often more efficient than conditionals. The more SpecialCase's involved, the greater the gain.
That's interesting to know. Of course in many other languages conditionals are more efficient than calls.
Does anyone have any data, or are we just discharging fear?