Рефакторинг метода с опциональными параметрами

Источник: «How to Refactor a Method With Optional Params»
В процессе рефакторинга и оптимизации легаси кода я столкнулся с несоответствием сигнатур метода, что вызывало проблемы с производительностью.

class PageRepository
{
...
public function find($pageId, $prerender = false)
{
...
if ($prerender === true) {
$page->prerender();
}
...
return $page;
}

public function findOrFail($pageId, $prerender = true)
{
$page = $this->find($pageId, $prerender);

if ($page === null) {
throw new Exception("Page $pageId not found.");
}

return $page;
}
...
}

Метод PageRepository::find имел опциональный параметр $prerender, с значением по умолчанию false. Проблема в том, что метод PageRepository::findOrFail имеет тот же необязательный параметр, но его значение по умолчанию равно true!

Если вы невнимательно читаете сигнатуры методов и значения параметров по умолчанию, вы можете легко ошибиться и выполнить пререндер страницы (а это дорогостоящая операция), не желая этого или не нуждаясь в этом.

Методы с опциональными параметрами-флагами могут быть потенциально опасны, и их трудно разглядеть.

Есть несколько вещей, которые можно сделать, чтобы они стали лучше и читабельнее.

1. Изменение значений по умолчанию опциональных параметров на одинаковые

Здесь всё просто. Просто измените опциональное значение по умолчанию $prerender в PageRepository::findOrFail на false (или наоборот).

2. Укажите опциональное значение параметра

Просто укажите значение в явном виде, чтобы любой, кто будет читать ваш код (даже через пару месяцев), знал, что оно было задано и использовано намеренно.

$pageRepository = new PageRepository();

$page = $pageRepository->find($pageId, false);

А ещё лучше — использовать переменную с семантическим значением при вызове метода.

$pageRepository = new PageRepository();

$page = $pageRepository->find($pageId, $prerender = false);

Знаю, что линтеры или IDE могут выделять параметр $prerender, но есть несколько способов справиться с ним, и оставляю на ваше усмотрение выбор способа, как с ним справиться.

3. Рефакторинг метода с опциональным параметром flag на два отдельных метода

Удалите вызов $page->prerender() из метода PageRepository::find и перенесите его в отдельный метод PageRepository::findAndPrerender.

class PageRepository
{
...
public function find($pageId, $prerender = false)
{
...
if ($prerender === true) {
$page->prerender();
}
...

return $page;
}

public function findAndPrerender($pageId)
{
$page = $this->find($pageId, true);

if ($page === null) {
throw new Exception("Page $pageId not found.");
}

return $page;
}
...
}

Таким образом, придётся осознанно выбирать, какая функциональность (или, скорее, какой побочный эффект) нужна.

Это был просто краткий совет по рефакторингу, который я обычно настойчиво предлагаю при проведении ревью кода, чтобы сделать код понятным и избежать путаницы.

Дополнительные материалы

Предыдущая Статья

Зачем использовать пользовательские исключения в PHP

Следующая Статья

Как использовать Git submodule