Null Object Pattern

by: George El., April 2019, Reading time: 2 minutes

In this post I will explain a pattern used in OOP, called Null Object Pattern. This pattern allows us to use our code as usual even if what we get back is a null object. Lets first see an example without using the pattern.

I will create a dictionary called Persons that will hold Person objects. The key for each object will be an integer. This way I can quickly get an object back if I know the key. Lets see the code.

Persons = {}

class Person():
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def printPerson(self):
        print(self.name, self.age)

def createPersons():
    for i in range(10):
        p = Person(f'name{i}', 20+i)
        Persons[i] = p

if __name__ == '__main__':
    createPersons()
    for i in range(12):
        p = Persons.get(i)
        if p is not None:
            p.printPerson()
        else:
            print("no such person")

createPersons creates ten persons and assigns them the name name0 to name9 and 20 to 29. Now if I try get object 10 and 11, they will be None, so I have to check with an if statement, if I get None or not.

name0 20
name1 21
name2 22
name3 23
name4 24
name5 25
name6 26
name7 27
name8 28
name9 29
no such person
no such person

If I have this kind of code in many parts I have to check many times if I get back None. This is not very convenient.

Now I will show you how to convert this code using a null object. I will create a new class that will represent the null Person.

class NullPerson():
    def __init__(self):
        self.name = "no such person"
    
    def printPerson(self):
        print(self.name)

Now each time I get None back I will return the NullPerson object. This will allow my code to continue to work

class Person():
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def printPerson(self):
        print(self.name, self.age)

class NullPerson():
    def __init__(self):
        self.name = "no such person"
    
    def printPerson(self):
        print(self.name)

def createPersons():
    for i in range(10):
        p = Person(f'name{i}', 20+i)
        Persons[i] = p

def getPerson(id):
    try:
        return Persons[id]
    except KeyError:
        return NullPerson()

if __name__ == '__main__':
    createPersons()
    for i in range(12):
        p = getPerson(i)
        p.printPerson()

the output is

name0 20
name1 21
name2 22
name3 23
name4 24
name5 25
name6 26
name7 27
name8 28
name9 29
no such person
no such person

so the output is the same, but I don’t have to check in my code if i get a None object or not.

comments powered by Disqus