blob: 61aadd41e048db088371efb62c4715a20a957aa2 [file] [log] [blame]
= Null Object Pattern
The http://en.wikipedia.org/wiki/Null_Object_pattern[Null Object Pattern] involves using a special object place-marker object representing null. Typically, if you have a reference to null, you can't invoke ++reference.field++ or ++reference.method()++. You receive the dreaded ++NullPointerException++. The null object pattern uses a special object representing null, instead of using an actual ++null++. This allows you to invoke field and method references on the null object. The result of using the null object should semantically be equivalent to __doing nothing__.
== Simple Example
Suppose we have the following system:
[source,groovy]
----
include::{projectdir}/src/spec/test/DesignPatternsTest.groovy[tags=null_object_simple_example,indent=0]
----
When run, this prints out ++1200++. Suppose now that we now invoke:
[source,groovy]
----
include::{projectdir}/src/spec/test/DesignPatternsTest.groovy[tags=null_object_simple_example2,indent=0]
----
If we now try to calculate ++biggestSalary++ again, we receive a null pointer exception.
To overcome this problem, we can introduce a ++NullJob++ class and change the above statement to become:
[source,groovy]
----
include::{projectdir}/src/spec/test/DesignPatternsTest.groovy[tags=null_object_simple_example3,indent=0]
----
This works as we require but it's not always the best way to do this with Groovy. Groovy's safe-dereference operator (++?.++) operator and null aware closures often allow Groovy to avoid the need to create a special null object or null class. This is illustrated by examining a groovier way to write the above example:
[source,groovy]
----
include::{projectdir}/src/spec/test/DesignPatternsTest.groovy[tags=null_object_simple_example4,indent=0]
----
Two things are going on here to allow this to work. First of all, ++max()++ is __'null aware'__ so that ++[300, null, 400].max() == 400++. Secondly, with the ++?.++ operator, an expression like ++p?.job?.salary++ will be equal to null if ++salary++ is equal to null, or if ++job++ is equal to null or if ++p++ is equal to null. You don't need to code a complex nested ++if ... then ... else++ to avoid a ++NullPointerException++.
== Tree Example
Consider the following example where we want to calculate size, cumulative sum and cumulative product of all the values in a tree structure.
Our first attempt has special logic within the calculation methods to handle null values.
[source,groovy]
----
include::{projectdir}/src/spec/test/DesignPatternsTest.groovy[tags=null_object_tree_example,indent=0]
----
If we introduce the null object pattern (here by defining the ++NullTree++ class), we can now simplify the logic in the ++size()++, ++sum()++ and ++product()++ methods. These methods now much more clearly represent the logic for the normal (and now universal) case. Each of the methods within ++NullTree++ returns a value which represents doing nothing.
[source,groovy]
----
include::{projectdir}/src/spec/test/DesignPatternsTest.groovy[tags=null_object_tree_example2,indent=0]
----
The result of running either of these examples is:
----
4
14
120
----
Note: a slight variation with the null object pattern is to combine it with the singleton pattern. So, we wouldn't write ++new NullTree()++ wherever we needed a null object as shown above. Instead we would have a single null object instance which we would place within our data structures as needed.