Problem Statement
When a system expects an object but receives a null or None value, it often leads to errors or requires many null checks scattered throughout the code. This makes the code complex, error-prone, and harder to maintain.
Jump into concepts and practice - no test required
This diagram shows the client code interacting with an abstract object interface, which can be either a real object or a null object that safely handles calls without side effects.
### Before Null Object pattern (with many null checks): class Animal: def make_sound(self): pass class Dog(Animal): def make_sound(self): print("Woof") class Cat(Animal): def make_sound(self): print("Meow") def animal_sound(animal): if animal is not None: animal.make_sound() else: print("No animal to make sound") # Usage animal_sound(Dog()) # Woof animal_sound(None) # No animal to make sound ### After applying Null Object pattern: class Animal: def make_sound(self): pass class Dog(Animal): def make_sound(self): print("Woof") class Cat(Animal): def make_sound(self): print("Meow") class NullAnimal(Animal): def make_sound(self): # Do nothing or print neutral message print("No animal present") def animal_sound(animal): animal.make_sound() # No null check needed # Usage animal_sound(Dog()) # Woof animal_sound(NullAnimal()) # No animal present
Null Object pattern in system design?class Logger {
log(message) { console.log(message); }
}
class NullLogger {
log(message) { /* do nothing */ }
}
function process(data, logger) {
logger.log('Start');
// process data
logger.log('End');
}
const logger = new NullLogger();
process('input', logger);