Patterns: Null Object

I've covered never having nulls and that's what the NullObject pattern is here to help us with.

The NullObject pattern is one that I tried to implement earlier in my career (mostly after reading about it) and never managed to get it to stick.

I now know that it never stuck because I never did it right. I got close. I got rid of the nulls; but never the Null check. I always had a IsNull method on my Null Objects... WHICH IS WRONG!

As I'm working with other developers and pushing them to use the NullObject pattern; I've seen them do exactly that.

IThingy thingy = TheThingy();
if(thingy.Exists()){
   thingy.Foo();
}

This happened at work when I was out for a few days. I came back to nested ifs checking the "status" of the NullObject.

This isn't to knock the other developers; they were trying. Proper OOP is new to them; the µObject style newer yet. They were trying to follow the practices and patterns I laid down.
They did it pretty well.

To foreshadow another topic - This is exactly why our industry needs mentorship programs. Being able to point out the silly mistakes in applying new concepts... I tried NullObject almost a decade ago... and because of a dumb misunderstanding - I'm only now using it. We need better information sharing about how to program effectively.

Anyway - They made a fantastic effort. I took away their "null check" and told them, "Now make it work". Took a couple hours of refactoring and being a navigator for their implementation; but we took 2 methods from 16 lines each to 2 lines each. All the if statements ... poof... gone. Two of the classes in our Chain of Command.... poof... not needed.

The NullObject pattern simplifies the code. It's one of the strongest 'code-simplification' patterns I've used.

NullObject is an invasive, but freeing, Pattern. It's a pattern that once you start using it; everything starts using it. In our refactor; we had something that was more like

IThingy thingy = TheThingy();
if(thingy.IsReal()){
   IFoo foo = thingy.Foo();
   if(foo.IsReal()){
       foo.Bar()
   }
}

As we created NullObjects we had to have them return NullObjects. Which then needed to return some NullObjects. It goes levels down; absolutely.
NullObjects spread through the code; but the code doesn't know it. The code starts to know even less about the objects it deals with. Can it be "invalid"? The code never has to check.
Once we introduced correct NullObjects; we had

TheThingy().Foo().Bar();

7 lines down to 1.

This massive simplification is due to the code being allowed to be dumb. What does it know? Next to nothing. It doesn't need to check status'. It doesn't need to branch. Complexity reduced => Better Maintainability. Lines of code reduced => Better Maintainability.

NullObject increases the maintainability of your code.

Taking the concept further

As I've been working with µObjects I've found that I can extend this idea of "just use it" to much more of the code.
I don't protect against what I call. I should not know about the conditions for it to execute.
What I'm calling should have in it's contract all the information required for IT to determine if IT should take action or not.
It's not respectful to make decisions for other objects. Trust your collaborators to ask for enough information to behave properly.
A quick example of this

if(foo.NotGonnaWork()) return; 
bar.OtherThing();

The correct way to do this is by trusting bar to only execute when required. This forces foo to be a collaborator of bar but, YES! Objects have collaborators.

bar.OtherThing(foo)

Now it's the responsibility of bar to know how to determine if it should run. We're treating it with respect and not trying to "protect it". Let it hurt, it'll evolve to protect itself.

Show Comments