# Decorator паттерн в Javascript

poster
В этом видео мы с вами разберем такой паттерн, как Decorator. Как и миксин, он является альтернативой наследования от класса к классу.
Понравилось? Поделитесь с друзьями!
Понравилось?
Поделитесь с друзьями!
Комментарии
Текст видео

В этом видео мы с вами разберем такой паттерн, как Decorator. Как и миксин, он является альтернативой наследования от класса к классу.

С помощью декоратора, мы можем динамически добавлять обьектам новые свойства и методы. То есть мы как бы заворачиваем наш обьект в декоратор, как в superclass.

Очень частый пример, когда нам выгодно использовать декораторы, это если нам понадобится для нашей системы огромного количество подклассов.

Например у нас есть базовый класс Coffee. И мы хотим создать дочерний класс Espresso, Latte, Cappuchino. И потом если мы хотим сделать каждый из них с сахаром нам нужно создавать класс EspressoWithSugar, который наследуется от Espresso, LatterWithSugar и так далее. У нас получается огромное количество дочерних классов, которых может быть огромное множество.

Эту проблему для нас может решить декоратор.

Давайте попробуем на реальном примере.

У нас есть класс Coffee, который создает нам кофе. И у него будет метод cost, который возвращает нам цену

class Coffee {
  cost () {
    return 5
  }
}

const coffee = new Coffee()
console.log(coffee.cost())

В браузере мы видим, что вывелась цена 5.

Теперь давай добавим декоратор sugar, который будет добавлять в кофе сахар, этим самым изменяя его цену.

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

const coffee = new Coffee()
sugar(coffee)
console.log(coffee)

После вызова декоратора обьект coffee будет мутирован. Теперь давайте опишем этот декоратор.

const sugar = coffee => {
  const cost = coffee.cost()
  coffee.cost = () => cost + 1
}
const coffee = new Coffee()
sugar(coffee)
console.log(coffee.cost())

Внутри функции мы получили стоимость кофе и переопределили функцию cost, которая теперь будет возвращать новую стоимость кофе, уже включая сахар.

То есть наш декоратор мутировал обьект, который мы в него передали. Но мы по прежнему можем создавать базовые экземпляры кофе, так как мы не меняли класс.

Давайте добавим его парочку декораторов. Например по размеру кофе будет изменятся цена.

const sugar = coffee => {
  const cost = coffee.cost()
  coffee.cost = () => cost + 1
}

const small = coffee => {
  const cost = coffee.cost()
  coffee.cost = () => cost - 1
}

const large = coffee => {
  const cost = coffee.cost()
  coffee.cost = () => cost + 1
}

const coffee = new Coffee()
sugar(coffee)
large(coffee)
console.log(coffee.cost())

Также ничто не мешает нам комбинировать декораторы внутри друг друга. Например мы хотим создать большой кофе с молоком.

Добавим декоратор withMilk и декоратор largeWithMilk

const withMilk = coffee => {
  const cost = coffee.cost()
  coffee.cost = () => cost + 2
}

const largeWithMilk = coffee => {
  large(coffee)
  withMilk(coffee)
  const cost = coffee.cost()

  coffee.cost = () => cost
}

В декораторе largeWithMilk мы сначала вызвали два других декоратора, тем самым изменяя функцию цены и потом вернули новую цену.

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

Если у вас возникли какие-то вопросы или комментарии, пишите их прямо под этим видео.

Только зарегистрированные пользователи могут оставлять комментарии.  Войдите, пожалуйста.
Weynemeynen
3 лет назад
Я одно не понимаю, почему кофе в долларах а не в рублях?
monsterlessons
3 лет назад
Наверное, потому что я не живу в России. Но хотя вы правы, надо было в евро делать.
Weynemeynen
3 лет назад
Следовательно в евро зоне. Из России/Украины снова утекли светлые мозги :( И какова там сейчас жизнь программиста?
monsterlessons
3 лет назад
Вы подняли мне настроение про "светлые мозги", спасибо! Я живу в Гамбурге (Германия) и переехал из Украины (Киев) 5 лет назад. Ответить в камменте как тут жить программиста сложно, так как я не знаю, какие конкретно моменты для вас важны. Мне норм. Поэтому мой список важных вещей - За деньгами в Европу не едут тк сениор в Киеве получает 3-5+ к$. В Гамбурге сениор получает 60-70к евро в год (это 3-3.5к евро) но исходя из уровня трат на numbeo вы можете понять сколько остается - 90% кейзов медстраховка бесплатная. Уровень медицины очень хороший особенно сравнивая в Украиной - Безопасность - Возможности, которых не существует в СНГ или они зачаточные - Акции/ETF/Биржи - Амазон (можно купить все что угодно из любой страны) - Платежная система (Stripe что я использую невозможен для резидентов СНГ. PS у него самые низкие коммисии) - Тоже самое с кучей сервисов которые доступны только для США и Европы - Другая жизнь/менталитет/приоритеты и скорость жизни - Социальная защищенность - Блукард = безвиз в ЕС + получение любой другой визы практически 100% P.S. Я могу еще миллион пунктов написать, но главным плюсом Киева была высокая зп, других плюсов у меня нет. Но так как плюс всего один то он не может закрыть все остальные P.P.S Мне лично корона показала, что деньги не так важны, а нормальная организация страны в условиях пандемии + намного более высокий шанс, что человека спасут показала, что я сделал правильный выбор
Weynemeynen
3 лет назад
Существенно. Спасибо за раскас.
vadimnikee
6 лет назад
Это невообразимо охренительно, спасибо за четкие курсы, сейчас как раз на собеседовании будут по патернам шпилить, вот к счастью наткнулся за день и есть время подготовится, ты топ!!
monsterlessons
6 лет назад
Спасибо)
spekt
6 лет назад
Доступно и понятно! топ!
monsterlessons
6 лет назад
На здоровье)
pretender
6 лет назад
Здравствуйте! Не совсем понял зачем так делали? const largeWithMilk = coffee => { large(coffee) withMilk(coffee) const cost = coffee.cost() // Эта строка coffee.cost = () => cost // И эта строка } Почему не делали так: const llargeWithMilk = coffee => { large(coffee); withMilk(coffee); }; Каждый же декоратор последовательно меняет coffee.cost. И на выходе получается тот же результат. Спасибо! Очень понятно и полезно.
monsterlessons
6 лет назад
Добрый день. Если вы посмотрите, что у вас будет за функция в coffee.cost после вызова largeWithMilk, то в вашем случае это () => cost + 2 То есть, у вас cost хранится не чистый возврат значение cost из замыкания, а сложение. Переопределив функцию, мы вернем чистый результат. Но вы правы, в обоих случаях мы получим тот же результат.
fromanywhere
6 лет назад
Всё же декоратор не предполагает мутации. Предполагается, что инстанс декоратора оборачивает («декорирует») инстанс оригинального класса, дополняя поведение, но не меняя оригинал
monsterlessons
6 лет назад
Ну так мы нигде и не мутировали исходный класс.
fromanywhere
6 лет назад
В примере мутирован экземпляр coffee, о чем явно говорится в тексте. Метод cost безвозвратно переопределяется, в то время как зона ответственности декоратора — лишь хранить ссылку на оригинал, а не менять его. Предлагается сравнить с https://jsfiddle.net/4oLn3zbz/
monsterlessons
6 лет назад
Я понял, что вы имеете ввиду. Вполне можно писать и так. Но в паттерне декоратор я ни разу не встречал, что мутировать инстанс запрещено и много примеров в интернете, которые я видел тоже мутировали инстанс. Например: https://addyosmani.com/blog/decorator-pattern
Артур Владимирович
6 лет назад
Согласен, с телефона читать одно удовольствие!
monsterlessons
6 лет назад
Спасибо, очень приятно.
Тема Маклаков
7 лет назад
Какой же у Вас красивый сайт. "Сделано с любовью". Вы большой молодец!!!
monsterlessons
7 лет назад
Спасибо большое. Стараюсь.
Sergey Illarionov
7 лет назад
Супер и долгожданно! Спасибо.
monsterlessons
7 лет назад
Был в отпуске, поэтому не было новых видео. Сейчас в обычном режиме.