В этом видео мы с вами разберем такой паттерн, как 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 мы сначала вызвали два других декоратора, тем самым изменяя функцию цены и потом вернули новую цену.
Итак паттер декоратор отлично подходит для добавления нового поведения нашим обьектам, без необходимости модифицировать поведение по умолчанию. Также мы избегаем необходимости создавать огромное количество подклассов.
Если у вас возникли какие-то вопросы или комментарии, пишите их прямо под этим видео.