3.7. Объект Null (Null Object)

3.7.1. Назначение

NullObject не шаблон из книги Банды Четырёх, но схема, которая появляется достаточно часто, чтобы считаться паттерном. Она имеет следующие преимущества:

  • Клиентский код упрощается

  • Уменьшает шанс исключений из-за нулевых указателей (и ошибок PHP различного уровня)

  • Меньше дополнительных условий — значит меньше тесткейсов

Методы, которые возвращают объект или Null, вместо этого должны вернуть объект NullObject. Это упрощённый формальный код, устраняющий необходимость проверки if (!is_null($obj)) { $obj->callSomething(); }, заменяя её на обычный вызов $obj->callSomething();.

3.7.2. Примеры

  • Объекты Null-логгер или Null-вывод поддерживают стандартный вид взаимодействий между объектами, даже если они ничего не делают

  • Null-обработчик в шаблоне Цепочка Обязанностей (Chain of Responsibilities)

  • Null-команда в шаблоне Команда (Command)

3.7.3. Диаграмма UML

Alt NullObject UML Diagram

3.7.4. Код

Вы можете найти этот код на GitHub

Service.php

 1<?php
 2
 3declare(strict_types=1);
 4
 5namespace DesignPatterns\Behavioral\NullObject;
 6
 7class Service
 8{
 9    public function __construct(private Logger $logger)
10    {
11    }
12
13    /**
14     * do something ...
15     */
16    public function doSomething()
17    {
18        // notice here that you don't have to check if the logger is set with eg. is_null(), instead just use it
19        $this->logger->log('We are in ' . __METHOD__);
20    }
21}

Logger.php

 1<?php
 2
 3declare(strict_types=1);
 4
 5namespace DesignPatterns\Behavioral\NullObject;
 6
 7/**
 8 * Key feature: NullLogger must inherit from this interface like any other loggers
 9 */
10interface Logger
11{
12    public function log(string $str);
13}

PrintLogger.php

 1<?php
 2
 3declare(strict_types=1);
 4
 5namespace DesignPatterns\Behavioral\NullObject;
 6
 7class PrintLogger implements Logger
 8{
 9    public function log(string $str)
10    {
11        echo $str;
12    }
13}

NullLogger.php

 1<?php
 2
 3declare(strict_types=1);
 4
 5namespace DesignPatterns\Behavioral\NullObject;
 6
 7class NullLogger implements Logger
 8{
 9    public function log(string $str)
10    {
11        // do nothing
12    }
13}

3.7.5. Тест

Tests/LoggerTest.php

 1<?php
 2
 3declare(strict_types=1);
 4
 5namespace DesignPatterns\Behavioral\NullObject\Tests;
 6
 7use DesignPatterns\Behavioral\NullObject\NullLogger;
 8use DesignPatterns\Behavioral\NullObject\PrintLogger;
 9use DesignPatterns\Behavioral\NullObject\Service;
10use PHPUnit\Framework\TestCase;
11
12class LoggerTest extends TestCase
13{
14    public function testNullObject()
15    {
16        $service = new Service(new NullLogger());
17        $this->expectOutputString('');
18        $service->doSomething();
19    }
20
21    public function testStandardLogger()
22    {
23        $service = new Service(new PrintLogger());
24        $this->expectOutputString('We are in DesignPatterns\Behavioral\NullObject\Service::doSomething');
25        $service->doSomething();
26    }
27}