Null object pattern
Eliminate null checks by providing a do-nothing implementation that conforms to the expected interface, making client code simpler and safer.
Introduction
The Null Object pattern represents an optional behavior with a real object that implements the same interface but performs a safe no-op or returns an identity value. The client can call the interface unconditionally instead of branching on whether a collaborator exists.
TL;DR / mental model
Choose the behavior once at the composition boundary: inject the real collaborator when it is needed, or a named no-op implementation when it is not. The null case becomes a type, not a repeated conditional.
The Problem It Solves
You're building an order processing service. Some orders have a discount applied, some don't. Some contexts need logging, some don't. Without a pattern, your code becomes an obstacle course of if (x != null) checks scattered across every method.
Six null checks across two methods, and the class only has two optional dependencies. Add a third (say, a notification channel) and you're looking at nine or more. Every new method repeats the same defensive ceremony. The real logic drowns in null-checking noise.
The deeper problem: this code violates the Open/Closed principle. To add "no discount" behavior, you don't create a new class. You litter the existing code with conditionals. That is the opposite of polymorphism.
Here is what changes when you apply the Null Object pattern.
Structure
The pattern is deceptively simple. You define an interface, create a real implementation, and create a "null" implementation that does nothing. The client code treats both identically.
NullLogger and NoDiscount are the null objects. They conform to the interface, so OrderService does not ask "do I have a real one or a fake one?" It just calls the method. Polymorphism replaces the repeated if != null checks in the client.
The null object provides a do-nothing default that eliminates conditional logic in the client.
Implementation
Continue Reading with Premium
Unlock this article and every other in-depth system design guide on the platform with SDEpedia Premium.
Related Articles
The strategy pattern extracts a family of algorithms behind an interface so the client can swap behaviors at runtime without touching the context class.
The state pattern encapsulates state-specific behavior into separate objects, eliminating large switch statements and making each state's logic independently testable.