How Not To Crash #7 sounds like a nifty cocktail -- perhaps something you’d enjoy after finishing your Corpse Reviver #2. But it’s the latest installment of Brent Simmons important series of notes on avoiding crashes in contemporary software.
This one concerns “Dealing With Nothing” or, more precisely, coping with situations where the object you’re processing is nothing at all. For example, you might have methods that display selected objects; what happens when nothing is selected? Or you might be displaying a shopping cart at checkout: what happens when the shopping cart is empty?
One technique Brent doesn’t mention is the null object pattern. In his example, we might call
[self doStuff:thing];
If doStuff:
expects thing to be an object and you give it nil, you might well crash and that’s a very bad thing to do and deplored by all right-thinking folk. Brent suggests that doStuff: should both assert that thing isn’t nil and guard against nil things, e.g.
NSParameterAssert(thing);
if (thing) {menuItem.title=thing;}
This is often a good idea. The first time I saw it, in John Daub’s PowerPlant, it seemed exorbitant: who could remember to include all those guards in every method? Soon, though, it becomes a reflex and you write (and read) the guards without needing to think about them.
If you do find yourself writing lots of if(thing)...
tests, though, you might be better off passing a Null Object -- a subclass of the expected class that does nothing, and does it quickly.
For example, Tinderbox has a class for LayoutPolicy -- objects that know how to lay out maps, or outlines, or treemaps. In the old days, before using the LayoutPolicy, we’d carefully check that the layout policy actually existed. This solves problems when you're building a new window, or closing an old one, or changing the view right in the middle of a screen update, but it does lead to an awful lot of code that simply makes sure the layout policy is where it ought to be.
So, instead of checking, Tinderbox initializes views with an instance of NoLayoutPolicy. If a view is being constructed and isn't quite ready to do real work, we can still call layoutPolicy->Prepare()
in perfect safety. If we’re about to disassemble a window, we can replace its layout policy with a fresh NoLayoutPolicy, confident that if someone tries to refresh the window we’re demolishing, there will be a NoLayoutPolicy standing ready to do nothing. Because there’s always a layout policy -- even if it’s only a NoLayoutPolicy -- we can take all those null check and dump them all in the trash.
We’ve got NoLayoutPolicy, NoDrawingPolicy, NotAnAttribute, NotANode. We’ve got plenty of nothing.