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

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

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

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

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

Стратегия (Паттерн)

Закрепим пройденную теорию на одном практическом примере, показывающем типичное применение полиморфизма подтипов.

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

Сумма страховки зависит от большого числа факторов. Некоторые из факторов могут влиять на сам процесс подсчёта стоимости, то есть они изменяют не значения в формуле подсчёта, а саму формулу.

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

Если решать эту задачу в лоб, то она будет выглядеть как большое месиво вычислений со множеством условных конструкций. Со временем такой код становится крайне тяжёлым для восприятия из-за большого числа состояний, которые надо удерживать в голове.

// Примеры с потолка
if (age < 18) {
  let cost = salary * age;

  if (country === 'uganda') { 
    cost = cost / 2;
  }
} else if (age >= 18 && age < 24) {
    // ...
}

Можно ли как-то сделать код понятнее и проще для восприятия? Иногда да. Из всех факторов, участвующих в расчёте, нужно попытаться найти те, которые влияют на вычисление глобально. Они проявляются как глобальный if на верхнем уровне. Предположим, что в случае страховки это возраст. То есть считаем что возраст определяет формулу расчёта стоимости страховки. Следующим шагом смотрим на ветки в этой условной конструкции и то, какие диапазоны там указаны. Допустим такую картину:

  • До 18
  • От 18 до 24
  • От 24 до 65
  • Старше 65

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

Теперь делаем переход от логической структуры к коду. Каждая возрастная группа - класс, отвечающий за вычисления стоимости для этой группы:

class LessThan18 {
  // Параметры, это те самые факторы, по которым строится вычисление
  calculate(params) {
    // тут считаем и возвращаем результат
  }
}

// Имя, конечно, так себе, в реальном коде стоит придумать что-нибудь говорящее
class MoreThan18AndLessThan24 {
  // Структура параметров должна 100% совпадать с остальными классами,
  // так как только в этом случае возможен полиморфизм
  calculate(params) {
      // тут считаем и возвращаем результат
  }
}

// Остальные классы

Главное, что мы получили - разделили процесс вычисления на независимые блоки кода, которые проще для восприятия. Каждый такой класс называется стратегией (вычисления). Очень важно то, что стратегия не является абстракцией, объектом с состоянием и временем жизни. Поэтому данные передаются не в конструктор, а в сам метод. По сути, это обычная функция (вычисление), которая упакована в класс только с одной целью — получить полиморфизм подтипов. Всё то же самое можно сделать, используя диспетчеризацию функций по ключам, при этом код будет проще.

Дальше возникает вопрос, а каким образом и где выбрать правильную реализацию, с которой нужно работать? Вариантов здесь несколько. Выбор реализации может быть делегирован внешнему коду, то есть если мы применяем инверсию зависимостей, то работаем уже с готовой стратегией:

calculateCost(strategy, params) {
  strategy.calculate(params);
}

Пока мы только ушли от проблемы, но не решили её. В любом случае, где-то будет код, который содержит либо условную конструкцию, либо реализует один из способов диспетчеризации, которые мы разобрали в предыдущих уроках. В самом простом случае этот код будет выглядеть так:

chooseCostInsuranceStrategy(user) {
  if (user.getAge() < 18) {
    return new LessThan18();
  } else if (/* ... */) {
    // some code
  }
}

strategy = chooseCostInsuranceStrategy(user);
strategy.calculate(params);

Как видно по примерам выше, кода с использованием стратегии будет больше, но не так много, если используется диспетчеризация функций по ключам в ассоциативном массиве. Это касается фактически всех ситуаций, в которых задействован полиморфизм подтипов в JavaScript. Это цена, которую придётся заплатить за разделение, упрощающее расширение кода и уменьшающее его сложность. С другой стороны, очень легко попасть в ловушку и, наоборот, сделать сложность кода выше, чем оно было до внедрения полиморфизма подтипов. Этот полиморфизм делает код многословным и излишне абстрактным, если применять его налево и направо. А расширение нужно не так часто, как об этом говорят. Более того, инвертировать зависимости можно по ходу действия, когда в этом появляется необходимость.


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

  1. Что такое expression problem
  2. Expression Problem (англ.)

<span class="translation_missing" title="translation missing: ru.web.courses.lessons.mentors.mentor_avatars">Mentor Avatars</span>

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

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

Для полного доступа к курсу нужна профессиональная подписка

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

Получить доступ
115
курсов
892
упражнения
2241
час теории
3196
тестов

Зарегистрироваться

или войти в аккаунт

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

  • 115 курсов, 2000+ часов теории
  • 800 практических заданий в браузере
  • 250 000 студентов

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

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

Логотип компании Альфа Банк
Логотип компании Rambler
Логотип компании Bookmate
Логотип компании Botmother

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

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

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