Null Object Pattern

NullObject is not a GoF design pattern but a schema which appears frequently enough to be considered a pattern


Purpose

NullObject is not a GoF (gang of four) design pattern but a schema which appears frequently enough to be considered a pattern. It has the following benefits:

  • Client code is simplified
  • Reduces the chance of null pointer exceptions
  • Fewer conditionals require less test cases

Methods that return an object or null should instead return an object or NullObject.

NullObjects simplify boilerplate code such as if (!is_null($obj)) { $obj->callSomething(); to just: $obj->callSomething(); by eliminating the conditional check in client code.

Examples
  • Null logger or null output to preserve a standard way of interaction between objects, even if the shouldn't do anything
  • Null handler in a Chain of Responsibilities pattern
  • Null command in a Command pattern
UML


Code

Service


namespace DesignPatterns\Behavioral\NullObject;

class Service
{
    public function __construct(private Logger $logger)
    {
    }

    /**
     * do something ...
     */
    public function doSomething()
    {
        // notice here that you don't have to check if the logger is set with eg. is_null(), instead just use it
        $this->logger->log('We are in ' . __METHOD__);
    }
}

Logger


namespace DesignPatterns\Behavioral\NullObject;

/**
* Key feature: NullLogger must inherit from this interface like any other loggers
*/
interface Logger
{
    public function log(string $str);
}

Null Logger


namespace DesignPatterns\Behavioral\NullObject;

class NullLogger implements Logger
{
    public function log(string $str)
    {
        // do nothing
    }
}

Print Logger


namespace DesignPatterns\Behavioral\NullObject;

class PrintLogger implements Logger
{
    public function log(string $str)
    {
        echo $str;
    }
}

Tests


public function testNullObject()
{
    $service = new Service(new NullLogger());
    $this->expectOutputString('');
    $service->doSomething();
}

public function testStandardLogger()
{
    $service = new Service(new PrintLogger());
    $this->expectOutputString('We are in DesignPatterns\Behavioral\NullObject\Service::doSomething');
    $service->doSomething();
}