Объект 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());
}
}