Привет, Хабр! Представляю вашему вниманию перевод статьи «Why should you return early?» автора Szymon Krajewski
В начале моего приключения в роли программиста мой код зачастую напоминал вермишель. В любых условных выражениях я только и делал, что сразу переходил к описанию верного исхода, оставляя на конец остальное. «Это работает, вот и все», — говорил я себе, а код продолжал расти, как на дрожжах. Тысячи написанных методов в итоге заставили меня задуматься, а не стоит ли поменять их внутреннюю логику, возвращая отрицательные результаты как можно раннее. Таким образом, я пришел к тому, что теперь называю правилом «неотложного провала».
Очевидно, что существует несколько подходов написания одной и той же функции. Например, как можно начать выполнение основной части сразу после положительного исхода условного оператора, так и можно сначала пробежаться по всем отрицательным исходам, возвращая ошибки из функции, а уже только потом перейти к основной логике. Иными словами, я открыл для себя разные стили написания условных конструкций.
$email
является валидным адресом, а $message
не является пустой строкой. В противном случае будет возвращена ошибка.function sendEmail(string $email, string $message)
{
if (filter_var($email, FILTER_VALIDATE_EMAIL)) {
if ($message !== '') {
return Mailer::send($email, $message);
} else {
throw new InvalidArgumentException('Cannot send an empty message.');
}
} else {
throw new InvalidArgumentException('Email is not valid.');
}
}
sendMail()
:return
недостаточно хорошо обозреваем. Конечно, приведенный пример очень прост и короток, но на практике в функции может наблюдаться огромное множество условий и возвратов значений. В этот момент и вступает в дело герой-спаситель «обратное условие».function sendEmail(string $email, string $message)
{
if (! filter_var($email, FILTER_VALIDATE_EMAIL)) {
throw new InvalidArgumentException('Email is not valid.');
}
if ($message === '') {
throw new InvalidArgumentException('Cannot send an empty message.');
}
return Mailer::send($email, $message);
}
else
после второго условного оператора, но, субъективно говоря, новый образец кода выглядит заметно чище и эстетичней. Явно, что «обратное условие» заслужило печеньку за помощь (прим. переводчика, тоже печеньку ему).«Ранний возврат» — это концепт написания функций таким образом, что ожидаемый положительный результат возвращается в конце, когда остальной код в случае расхождения с целью функции должен завершить ее выполнение настолько раньше, насколько возможно.Вы не поверите, насколько долго я думал над данным определением, но в любом случае концепт является обобщенным. Что такое «ожидаемый положительный результат», «завершение выполнения» и «цель функции»? Постараюсь описать это в дальнейшем.
sendMail
, можно уверенно сказать, что целью функции была отправка сообщения на указанный почтовый адрес. Это и есть «ожидаемый положительный результат», иными словами «путь к счастью».function matrixAdd(array $mA, array $mB)
{
if (! isMatrix($mA)) {
throw new InvalidArgumentException("First argument is not a valid matrix.");
}
if (! isMatrix($mB)) {
throw new InvalidArgumentException("Second argument is not a valid matrix.");
}
if (! hasSameSize($mA, $mB)) {
throw new InvalidArgumentException("Arrays have not equal size.");
}
return array_map(function ($cA, $cB) {
return array_map(function ($vA, $vB) {
return $vA + $vB;
}, $cA, $cB);
}, $mA, $mB);
}
updatePostAction
в контроллере PostController
:/* PostController.php */
public function updatePostAction(Request $request, $postId)
{
$error = false;
if ($this->isGranded('POST_EDIT')) {
$post = $this->repository->get($postId);
if ($post) {
$form = $this->createPostUpdateForm();
$form->handleRequest($post, $request);
if ($form->isValid()) {
$this->manager->persist($post);
$this->manager->flush();
$message = "Post has been updated.";
} else {
$message = "Post validation error.";
$error = true;
}
} else {
$message = "Post doesn't exist.";
$error = true;
}
} else {
$message = "Insufficient permissions.";
$error = true;
}
$this->addFlash($message);
if ($error) {
$response = new Response("post.update", ['id' => $postId]);
} else {
$response = new RedirectResponse("post.index");
}
return $response;
}
/* PostController.php */
public function updatePostAction(Request $request, $postId)
{
$failResponse = new Response("post.update", ['id' => $postId]);
if (! $this->isGranded('POST_EDIT')) {
$this->addFlash("Insufficient permissions.");
return $failResponse;
}
$post = $this->repository->get($postId);
if (! $post) {
$this->addFlash("Post doesn't exist.");
return $failResponse;
}
$form = $this->createPostUpdateForm();
$form->handleRequest($post, $request);
if (! $form->isValid()) {
$this->addFlash("Post validation error.");
return $failResponse;
}
$this->manager->persist($post);
$this->manager->flush();
return new RedirectResponse("post.index");
}
else
условия.function reverse($string, $acc = '')
{
if (! $string) {
return $acc;
}
return reverse(substr($string, 1), $string[0] . $acc);
}
false
:public function setUrl($url)
{
if (! $url) {
return;
}
$this->url = $url;
}
public function setUrl($url)
{
if ($url) {
$this->url = $url;
}
}
break
return
, не всегда удобно понять, откуда был получен результат. Но до тех пор, пока вы используете данный подход для завершения некорректных состояний функции, все будет в порядке. Ах, да, пишете ли вы тесты?return
операторовfunction nextState($currentState, $neighbours)
{
$nextState = 0;
if ($currentState === 0 && $neighbours === 3) {
$nextState = 1;
} elseif ($currentState === 1 && $neighbours >= 2 && $neighbours <= 3) {
$nextState = 1;
}
return $nextState;
}
function nextState($currentState, $neighbours)
{
if ($currentState === 0 && $neighbours === 3) {
return 1;
}
if ($currentState === 1 && $neighbours >= 2 && $neighbours <= 3) {
return 1;
}
return 0;
}
Следуйте пути к успеху и реагируйте в случае ошибок.
К сожалению, не доступен сервер mySQL