3.6. Pusty obiekt (Null Object)

3.6.1. Przeznaczenie

Pusty Obiekt nie jest wzrorcem zaproponowanym przez Bandę Czterech, niemniej jednak jest na tyle popularny, że może być uznany za wzorzec. Użycie tego wzorca niesie następujące korzyści:

  • uproszcza kodu programu,
  • zmniejsza prawdopodobieństwo wystąpienia błędu null pointer exception,
  • eliminuje zbędne lub powtarzające się warunki, które wpływają na zmniejszenie liczby przypadków testowych,

Metoda, która zwraca obiekt lub null powinna zwracać obiekt lub Obiekt Pusty (NullObject). NullObject w znaczący sposób upraszcza kod z postaci if (!is_null($obj)) { $obj->callSomething(); } do $obj->callSomething(); poprzez eliminację warunku w kodzie klienta.

3.6.2. Przykłady

  • Symfony2: NullLogger w ramach profilera.
  • Symfony2: NullOutput w ramach komponentu Symfony/Coonsole.
  • Obsługa NULL we wzrocu Łańcuch zobowiązań.
  • Polecenie NULL we wzorcu Polecenie.

3.6.3. Diagram UML

Alt NullObject UML Diagram

3.6.4. Kod

Ten kod znajdziesz również na GitHub.

Service.php

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?php

namespace DesignPatterns\Behavioral\NullObject;

class Service
{
    /**
     * @var LoggerInterface
     */
    private $logger;

    /**
     * @param LoggerInterface $logger
     */
    public function __construct(LoggerInterface $logger)
    {
        $this->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__);
    }
}

LoggerInterface.php

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<?php

namespace DesignPatterns\Behavioral\NullObject;

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

PrintLogger.php

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<?php

namespace DesignPatterns\Behavioral\NullObject;

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

NullLogger.php

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<?php

namespace DesignPatterns\Behavioral\NullObject;

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

3.6.5. Testy

Tests/LoggerTest.php

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?php

namespace DesignPatterns\Behavioral\NullObject\Tests;

use DesignPatterns\Behavioral\NullObject\NullLogger;
use DesignPatterns\Behavioral\NullObject\PrintLogger;
use DesignPatterns\Behavioral\NullObject\Service;
use PHPUnit\Framework\TestCase;

class LoggerTest extends TestCase
{
    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();
    }
}