Нормализация данных — подход, с помощью которого можно не только упростить логику кода, но и сделать сам код короче. Его принцип работы состоит в приведении данных к общему виду перед основным алгоритмом обработки этих данных. Посмотрим, как это работает на простом примере.
Представьте себе такую задачу: нужно извлечь из HTML все ссылки и загрузить их содержимое. Для простоты допустим, что HTML уже проанализирован, а ссылки собраны в массив. Кроме самих ссылок понадобится имя домена, на котором был расположен этот HTML. Это нужно для загрузки относительных ссылок, то есть тех ссылок, у которых не указан домен в HTML, например, /about. Ниже пример кода, решающего задачу в лоб:
// Кода здесь мало и код можно было бы оставить так,
// но именно на таких маленьких примерах и нужно отрабатывать хорошие практики
import axios from 'axios'; // http-клиент
// Эта функция анализирует HTML и извлекает ссылки
const getLinksFromHTML = /* определение функции */;
const domainName = /* тут имя домена, на котором расположен HTML */;
const html = /* сам html */;
// Подготовка данных
const links = getLinksFromHTML(html);
// Здесь основной алгоритм работы
const promises = links.map((link) => {
// Определяем тип ссылки. Если домена нет, значит используем текущий
// Сама проверка упрощена. Для правильного анализа придется попотеть
if (link.startsWith('/')) {
return axios.get(`${domainName}/${link}`);
}
return axios.get(link);
});
// Выполняем запросы параллельно
const responses = await Promise.all(promises);
Обратите внимание на чёткое разделение двух этапов. У нас есть место, где готовятся данные, а есть место, где выполняется основная логика. В данном случае обе эти части предельно маленькие, но в реальных приложениях они могут содержать десятки строк, множество условных конструкций и циклов. Большую часть кода можно разделить как минимум на эти два этапа.
Подписывайтесь на канал Кирилла Мокевнина в Telegram — чтобы узнать больше о программировании и профессиональном пути разработчика
Самое примечательное в этом коде — условие, проверяющее относительность ссылки. Посмотрите внимательно на код, который внутри. Он не выполняет какую-то другую особую логику. Всё, что он делает — приводит данные к нужному для обработки виду. Именно здесь и подходит нормализация данных. Вместо того, чтобы строить ссылку по условию, код можно изменить так, чтобы ссылки были полными ещё до попадания в эту часть программы.
import axios from 'axios';
import { URL } from 'url';
const getLinksFromHTML = /* определение функции */;
const domainName = /* тут имя домена на котором расположен HTML */;
const html = /* сам html */;
const links = getLinksFromHTML(html);
// Возможно эту логику имеет смысл поместить в getLinksFromHTML
const urls = links.map((link) => new URL(link, domainName));
// toString преобразует объект обратно в строку
const promises = urls.map((url) => axios.get(url.toString()));
const responses = await Promise.all(promises);
В чём хитрость этого решения? Встроенный модуль url в JavaScript содержит специальную функцию-конструктор URL, с помощью которой создается объект, описывающий конкретный url. Внутри этого объекта есть информация о всех частях адреса. Так их удобнее извлекать и даже менять. У этой функции есть второй параметр — базовый адрес, хост, который будет использоваться в том случае, когда ссылка относительная:
(new URL('/pages/about', 'https://ru.hexlet.io')).toString();
// 'https://ru.hexlet.io/pages/about'
// Если адрес абсолютный то ничего не ломается!
(new URL('https://yandex.ru/pages/about', 'https://ru.hexlet.io')).toString();
// 'https://yandex.ru/pages/about'
Использование готовых инструментов — идеальное решение задачи. Даже если бы у нас не было такого модуля, как url, все равно имело бы смысл делать предварительную нормализацию данных. Как правило, в живых проектах обработка — это больше чем одна условная конструкция. Одни и те же данные могут использоваться много раз и по-разному. Такой код очень легко обрастает условиями и дублированием.
Кстати, в примере выше мы получили дополнительное преимущество — убрали прямое использование строк.