Guard Expression — подход, который в некоторых случаях помогает снизить уровень вложенности функции и сэкономить ресурсы — сделать так, чтобы программа не выполнялась при пограничных случаях. На примерах разбираем, в чем суть подхода и в каких случаях его можно применять. Стоит отметить, что этот текст предназначен для тех, кто уже умеет создавать функции.
В этой статье мы сосредоточимся на основных моментах, которые характеризуют этот подход: в частности, на возможности отсекать крайние случаи и снижать уровень вложенности.
Цикломатическая сложность
Сложность функций может сильно отличаться: чем больше в ней проверок и условий, тем сложнее стороннему программисту ее читать и понимать. Сложность бывает разных типов: например, необходимая (когда без большого количества независимых условий не обойтись) и случайная (когда она зависит от навыков программиста и нужен рефакторинг).
Частично оценить их позволяет цикломатическая сложность — количество линейно независимых маршрутов через программный код. Эта мера была разработана Томасом Дж. Маккейбом в 1976 году. Если исходный код не содержит никаких точек ветвления или циклов, то сложность равна единице, поскольку есть только один маршрут следования через код. Но в ситуации, когда используются условия и циклы, цикломатическая сложность будет выше единицы.
// Complexity: 1
const f1 = () => {
console.log('Hello');
};
// Complexity: 2
const f2 = (condition) => {
if (condition) {
console.log('Hello');
}
}
// Complexity: ?
const f3 = (condition, language) => {
message = ''
if (condition) {
switch (language) {
case 'de':
message = 'Guten Tag'
case 'es':
message = 'Hola'
case 'fr':
message = 'Bonjour'
default:
message = 'Hello';
}
}
console.log(message);
}
В этом случае на помощь приходит Guard Expression. Этот подход (или паттерн), помогает лучше структурировать код, сделать его более простым и понятным, иногда снизить цикломатическую сложность. В качестве примера рассмотрим функцию, которая принимает на вход пол и возраст. Для людей старше 18 в зависимости от пола она возвращает строку yes
или no
. Для всех остальных — null
:
const f = (age, sex) => {
if (age >= 18) {
if (sex === 'male') {
return 'yes';
}
if (sex === 'female') {
return 'no';
}
}
return null;
}
Эту функцию можно сделать проще, разбив ее и добавив условие «вернуть null, если младше 18 лет». Оно гораздо более простое и очевидное, потому поставим его первым:
const f = (age, sex) => {
if (age < 18) {
return null;
}
if (sex === 'male') {
return 'yes';
}
if (sex === 'female') {
return 'no';
}
}
Уровень вложенности снизился. В такой реализации функции сложнее ошибиться: все, что пишется ниже guard expression (первая проверка в данном случае), попадает под требование «от 18 и старше». В первом примере код для этого условия нужно не забывать вставить внутрь соответствующего условия.
Читайте также: Как сохранять фокус на протяжении всего обучения: советы от Хекслета
Отсечение пограничных случаев
Вторая задача Guard Expression — сразу же обработать пограничные случаи. Речь идет о дополнительных проверках, которые могут усложнить и удлинить код, но не позволят функции или программе выполняться при определенных условиях.
const average = (numbers) => {
// Если массив чисел пустой, то возвращаем сразу 0
if (numbers.length === 0) {
return 0;
}
result = 0;
for (const number of numbers) {
result += number;
}
return result / numbers.length;
}
console.log(average([1, 2, 3, 4, 5]));
// => 3
Рассмотрим это на примере решения задачи по проверке числа на простоту. Ее условие звучит так: простое число — это натуральное число, которое делится только на 1 и себя. 0 и 1 - не простые числа.
0 и 1 — это и есть крайние случаи: в функции нужно написать правило проверки только тех чисел, которые выше единицы. В коде это будет выглядеть так:
const isPrime = (num) => {
if (num < 2) {
return false;
}
for (let i = 2; i <= Math.sqrt(num); i += 1) {
if (num % i === 0) {
return false;
}
}
return true;
};
Еще один пример: в проекте Hexlet SICP есть функция, которая отмечает прохождение пользователем упражнения. Она работает только для авторизованных пользователей, которые ранее не выполняли упражнение. Крайние случаи — отсутствие авторизации и прохождение упражнения два и более раз:
class ExerciseService
{
public function completeExercise(User $user, Exercise $exercise): void
{
if ($user->hasCompletedExercise($exercise) || $user->isGuest()) {
return;
}
$user->exercises()->syncWithoutDetaching($exercise);
$this->activityService->logCompletedExercise($user, $exercise);
}
}
Никогда не останавливайтесь: В программировании говорят, что нужно постоянно учиться даже для того, чтобы просто находиться на месте. Развивайтесь с нами — на Хекслете есть сотни курсов по разработке на разных языках и технологиях