Null Object
Replace `null` with a real object that implements the same interface but does nothing (or does something safely neutral), so callers never need a null-check.
The problem
A support ticket may or may not have someone assigned to it. Perfectly ordinary situation,
except every single piece of code that touches assignee now has to ask first.
One check is fine. The tenth one, written by someone who has never seen the first nine, is
where the bug lives - the guard that got forgotten, the NullPointerException three months
into production, the code review comment that says "did you check for unassigned tickets
here?" for the hundredth time.
The solution
Give "nobody assigned" its own class, and make that class implement the exact same interface
as a real assignee. UnassignedSlot.notify() simply does nothing. Every caller that used to
null-check now calls assignee.notify(ticket) unconditionally, and it is always safe,
because assignee is never actually null - it is either a working handler or a handler
that quietly declines to do anything.
The field never needs to be checked again, because it never needs to be absent. It always holds something that answers to the interface; the two implementations just answer differently.
- 1A new ticket arrives with no obviously right human for it yet.
- 2The router looks for a human match and finds none this time.
- 3Instead of leaving the field null, the router plugs in a do-nothing stand-in that still speaks the Assignee interface.
- 4Later, an unrelated code path escalates the ticket without knowing or caring whether anyone is assigned.
- 5Ticket calls notify() through the interface, oblivious to which concrete class answers.
- 6UnassignedSlot's notify() does nothing at all. The call succeeds, nothing happens, no exception ever threatens the caller.
- 7Some time later a human actually claims the queue. Same field, same interface, no code at the call site changes.
- 8Same line of code as before, but now it pages a real person. Ticket never found out the difference.
Structure
Notice Ticket has exactly one relationship in this diagram: it uses Assignee, the
interface. It has no relationship to HumanAgent or UnassignedSlot at all - it cannot
tell them apart, and that is the entire design.
Code
Same example three ways: a ticket that always has an assignee, real or not.
When to use it
- A collaborator field is legitimately optional (no handler, no logger, no discount) and every consumer of that field currently null-checks before using it.
- The "do nothing" behavior is genuinely safe - there is no case where silently skipping the operation could be mistaken for success when it matters.
Pitfalls
- Hiding a real problem. If "unassigned" is itself something the business needs to notice and act on, a Null Object that swallows the call is the wrong tool - it makes the absence invisible exactly where visibility mattered.
- Overreach. Not every optional field wants a full class hierarchy. A single
Optionalor a default value is sometimes just simpler than standing up an interface and an implementation for it. - Debuggability. "Nothing happened" and "something happened but did nothing useful" look identical from outside. Logging inside the null object (carefully, so it stays inert) can save an afternoon.
Don't confuse it with
- State. Both look like "an object representing emptiness that implements a shared interface." State exists to drive transitions between meaningfully different behaviors; Null Object exists purely to make one particular case harmless. If your empty state needs to become a different state later, you are already doing State.
- Strategy. A no-op strategy and a Null Object can be byte-for-byte identical code. The distinction is intent: Strategy is chosen because the algorithm genuinely varies; Null Object is chosen because "there is nothing here" needed to stop being a special case.
- Default parameter values. Passing
0or""as a default is not this pattern - Null Object specifically replaces an object reference that would otherwise need a null-check before its methods are called.
Check yourself
What does a Null Object actually eliminate?