# 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 мы сначала вызвали два других декоратора, тем самым изменяя функцию цены и потом вернули новую цену.

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

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

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