Допустим, мы проектируем пакетный обработчик команд. Чтобы узнавать о каждой успешной операции, добавим простой аргумент-слушатель
Теперь можно, например, инкрементировать прогресс-бар при вызове из консольной команды.
Однако не всегда слушатель будет нужен, поэтому для простоты контракта сделаем его необязательным аргументом. Решение "в лоб":
👹 Fatal error: Default value for parameters with callable type can only be NULL.
Эхх, видимо, без null здесь никак не обойтись. Но мы не сдаемся и красиво комбинируем.
🎉 Ура, так работает.
Плюсы этого подхода по сравнению с if:
• лаконичность: 1 строка вместо 3;
• простота восприятия: одно выражение в начале функции имеет меньшую цикломатическую сложность, чем условие в цикле;
• универсальность: легко переиспользовать во всех подобных ситуациях.
$onEach
./**
* @template T of object
* @psalm-param iterable<T> $commands
* @psalm-param callable(T): void $onEach
*/
function handleBatch(iterable $commands, callable $onEach): void
{
foreach ($commands as $command) {
// ...
$onEach($command);
}
}
Теперь можно, например, инкрементировать прогресс-бар при вызове из консольной команды.
handleBatch($commands, static function () use ($progressBar): void {
$progressBar->advance();
});
Однако не всегда слушатель будет нужен, поэтому для простоты контракта сделаем его необязательным аргументом. Решение "в лоб":
?callable $onEach = null
и потом if (null !== $onEach) { $onEach($command) }
.
А теперь применим паттерн NullObject. Для этого добавим в проектный functions.php
элементарную function void(): void {}
и попробуем её в качестве значения по умолчанию в сигнатуре обработчика: callable $onEach = 'void'
.👹 Fatal error: Default value for parameters with callable type can only be NULL.
Эхх, видимо, без null здесь никак не обойтись. Но мы не сдаемся и красиво комбинируем.
function handleBatch(iterable $commands, ?callable $onEach = null): void
{
$onEach ??= 'void';
// ...
}
🎉 Ура, так работает.
Плюсы этого подхода по сравнению с if:
• лаконичность: 1 строка вместо 3;
• простота восприятия: одно выражение в начале функции имеет меньшую цикломатическую сложность, чем условие в цикле;
• универсальность: легко переиспользовать во всех подобных ситуациях.