null-object Объект Null (англ. Null Object) — это объект с определенным нейтральным (“null”) поведением. Шаблон проектирования Null Object описывает использование таких объектов и их поведение (или отсутствие такового).

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

  • Клиентский код упрощается;
  • Уменьшает шанс исключений из-за нулевых указателей (и ошибок PHP различного уровня);
  • Меньше дополнительных условий — значит меньше тесткейсов.

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

  • NullWorker.php
                  
    <?php
    
    namespace DesignPatterns\Behavioral\NullObject;
    
    class NullWorker implements WorkerInterface
    {
        const MSG = 'NULL OBJECT';
    
        public function isNull(): bool
        {
            return true;
        }
    
        public function makeSomethink()
        {
            return self::MSG;
        }
    }
    
                  
  • Worker.php
                  
    <?php
    
    namespace DesignPatterns\Behavioral\NullObject;
    
    class Worker implements WorkerInterface
    {
        const MSG = 'Worker make somethink';
    
        public function isNull(): bool
        {
            return false;
        }
    
        public function makeSomethink()
        {
            return self::MSG;
        }
    }
    
                  
  • WorkerInterface.php
                  
    <?php
    
    namespace DesignPatterns\Behavioral\NullObject;
    
    interface WorkerInterface
    {
        public function isNull(): bool;
        public function makeSomethink();
    }
    
                  
  • WorkerPool.php
                  
    <?php
    
    namespace DesignPatterns\Behavioral\NullObject;
    
    class WorkerPool
    {
        /**
         * @var WorkerInterface[]
         */
        protected $pool = [];
    
        public function __construct(...$workers)
        {
            $this->pool = $workers;
        }
    
        public function getPool(int $key): WorkerInterface
        {
            return $this->pool[$key] ?? new NullWorker();
        }
    
        public function makeSomethink(int $key = null)
        {
            return $this->getPool($key)->makeSomethink();
        }
    }
    
                  
  • Test case:

    WorkerPoolTest.php
      
    <?php
    
    namespace DesignPatterns\Tests\Behavioral\NullObject;
    
    use DesignPatterns\Behavioral\NullObject\NullWorker;
    use DesignPatterns\Behavioral\NullObject\Worker;
    use DesignPatterns\Behavioral\NullObject\WorkerPool;
    
    class WorkerPoolTest extends \PHPUnit_Framework_TestCase
    {
        public function testNullObject()
        {
            $worker = new Worker();
            $pool = new WorkerPool($worker);
    
            $this->assertEquals(Worker::MSG, $pool->makeSomethink(0));
            $this->assertEquals(false, $worker->isNull());
            $this->assertEquals(false, $pool->getPool(0)->isNull());
            $this->assertEquals(NullWorker::MSG, $pool->makeSomethink(1));
            $this->assertEquals(true, $pool->getPool(1)->isNull());
        }
    }