Зарегистрируйтесь для доступа к 15+ бесплатным курсам по программированию с тренажером

Уровневое проектирование JS: Абстракция с помощью данных

Рассмотрим ещё одну простую систему — рациональные числа и операции над ними. Напомню, что рациональным называют число, которое может быть представлено в виде дроби a/b, где a — это числитель дроби, b — знаменатель дроби. Причём b не должно быть нулём, поскольку деление на ноль не допускается.

Рациональные числа в JS не поддерживаются, поэтому абстракцию для них создадим самостоятельно. Как обычно, нам понадобятся конструктор и селекторы:

const num = makeRational(1, 2); // создали рациональное число "одна вторая"
const numer = getNumer(num); // 1
const denom = getDenom(num); // 2

С помощью трёх функций мы определили рациональное число. Одна функция (конструктор) собирает его из частей, другие (селекторы) позволяют каждую часть извлечь. Чем при этом, с точки зрения языка, является num — абсолютно не важно. Хоть функцией (и такое возможно), хоть массивом или объектом. Во внутренней реализации можно даже использовать строки:

const makeRational = (numer, denom) => `${numer}/${denom}`;

const getNumer = (rational) => rational.split('/')[0];

const getDenom = (rational) => rational.split('/')[1];

console.log(makeRational(10, 3)); // => 10/3

Несмотря на то, что мы научились представлять рациональные числа, эта абстракция сама по себе малоприменима. Абстракция становится полезна тогда, когда появляется возможность оперировать ей. Для рациональных чисел базовыми операциями можно считать арифметические, например, сложение, вычитание или умножение. Умножение рациональных чисел — самая простая операция. Для её выполнения нужно перемножить числители и знаменатели:

3/4 * 4/5 = (3 * 4)/(4 * 5) = 12/20

Самое интересное начинается в процессе реализации. Если предположить, что реальная структура рационального числа выглядит так: { numer: 2, denom: 3 }, то, чисто технически, решение может быть таким:

const mul = (rational1, rational2) => {
  return {
    numer: rational1['numer'] * rational2['numer'],
    denom: rational1['denom'] * rational2['denom']
  };
};

С точки зрения вызывающего кода всё нормально, абстракция сохранена. На вход в mul подаются рациональные числа, на выходе — рациональное число. А вот внутри никакой абстракции нет, обращение с рациональными числами строится на основе знания их устройства. Любое изменение внутренней реализации рациональных чисел потребует переписывания всех операций, работающих с рациональными числами напрямую — то есть без селекторов или конструктора. Данный код нарушает принцип одного уровня абстракции (single layer abstraction).

При разработке сложных систем используется подход — уровневое проектирование. Он заключается в том, что системе придаётся структура при помощи последовательных уровней. Каждый из уровней строится путём комбинации частей, которые на данном уровне рассматриваются как элементарные. Части, которые строятся на каждом уровне, работают как элементарные на следующем уровне.

Уровневое проектирование пронизывает всю технику построения сложных систем. Например, при проектировании компьютеров резисторы и транзисторы сочетаются (и описываются при помощи языка аналоговых схем), и из них строятся и-, или- элементы и им подобные, служащие основой языка цифровых схем. Из этих элементов строятся процессоры, шины и системы памяти, которые в свою очередь служат элементами в построении компьютеров при помощи языков, подходящих для описания компьютерной архитектуры. Компьютеры, сочетаясь, дают распределённые системы, которые описываются при помощи языков описания сетевых взаимодействий, и так далее. (c) SICP

const mul = (rational1, rational2) => {
  return makeRational(
    getNumer(rational1) * getNumer(rational2),
    getDenom(rational1) * getDenom(rational2)
  );
};

В нашем примере базовым уровнем являются типы, встроенные в сам язык: числа и объекты. На их основе сформирован уровень для представления рациональных чисел: makeRational, getDenom, getNumer. Затем — уровень, на котором реализованы арифметические операции над рациональными числами: сложение, вычитание, умножение и так далее.

Подчеркну, что речь идёт про реализацию самих уровней. Например, операция сложения полностью опирается на конструктор и селекторы, но ничего не знает и не может знать про внутреннее устройство самих рациональных чисел. С другой стороны, это не значит, что в одном месте не могут появиться функции из разных уровней. Могут и это нормально во многих случаях. Например:

const f = (rational1, rational2) => {
  const rational3 = sum(rational1, rational2);
  const denom = getDenom(rational3);
  const numer = getNumer(rational3);
  console.log(`Denom: ${denom}`);
  console.log(`Numer: ${numer}`);
};

Аватары экспертов Хекслета

Остались вопросы? Задайте их в разделе «Обсуждение»

Вам ответят команда поддержки Хекслета или другие студенты.

Ошибки, сложный материал, вопросы >
Нашли опечатку или неточность?

Выделите текст, нажмите ctrl + enter и отправьте его нам. В течение нескольких дней мы исправим ошибку или улучшим формулировку.

Что-то не получается или материал кажется сложным?

Загляните в раздел «Обсуждение»:

  • задайте вопрос. Вы быстрее справитесь с трудностями и прокачаете навык постановки правильных вопросов, что пригодится и в учёбе, и в работе программистом;
  • расскажите о своих впечатлениях. Если курс слишком сложный, подробный отзыв поможет нам сделать его лучше;
  • изучите вопросы других учеников и ответы на них. Это база знаний, которой можно и нужно пользоваться.
Об обучении на Хекслете

Для полного доступа к курсу нужен базовый план

Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.

Получить доступ
900
упражнений
2000+
часов теории
3200
тестов

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно.

  • 120 курсов, 2000+ часов теории
  • 900 практических заданий в браузере
  • 360 000 студентов
Отправляя форму, вы соглашаетесь c «Политикой конфиденциальности» и «Условиями оказания услуг»

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff
Рекомендуемые программы

С нуля до разработчика. Возвращаем деньги, если не удалось найти работу.

Иконка программы Фронтенд-разработчик
Профессия
Разработка фронтенд-компонентов веб-приложений
27 октября 8 месяцев
Иконка программы Node.js-разработчик
Профессия
Разработка бэкенд-компонентов веб-приложений
27 октября 8 месяцев

Есть вопрос или хотите участвовать в обсуждении?

Зарегистрируйтесь или войдите в свой аккаунт

Отправляя форму, вы соглашаетесь c «Политикой конфиденциальности» и «Условиями оказания услуг»