Как стать автором
Обновить
1259.47
OTUS
Цифровые навыки от ведущих экспертов

Композиция > Наследование за 4 минуты

Время на прочтение4 мин
Количество просмотров9.6K
Автор оригинала: Gavin Macken

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

Наследование

Чтобы лучше понять, почему мы можем предпочесть композицию наследованию, давайте сначала рассмотрим наследование в Javascript, а именно в ES6. Ключевое слово extends используется в объявлениях или выражениях для создания класса, который является дочерним по отношению к другому.

class Plant{
 constructor(name){
  this.name = name
 }
 
 water(){
    console.log("Water the " + this.name)
 }
 
 repot(){
    console.log( "Repot the " + this.name)
 }harvest(){
    console.log("Harvest the " + this.name)
  }
}class Vegetable extends Plant {
  constructor(name, size, health){
   super(name)
   this.health = health;
  }
}class Flower extends Plant {
  constructor(name, size, health){
   super(name)   
   this.health = health;
  }
}class Fruit extends Plant {
  constructor(name, size, health){
   super(name)
   this.health = health;
  }
}

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

Метод water является общим для экземпляров Flower, Vegetable и Fruit, что полезно, поскольку все они нуждаются в поливе (watered), но нет необходимости, чтобы экземпляр Flower имел доступ к методу harvest (сбору урожая), а так как мои овощи высажены в землю, поэтому нет причин, чтобы они имели доступ к методу repot (пересадка).

Ассоциации должны выглядеть следующим образом:

  • Фрукты поливаются, пересаживаются, собираются.

  • Цветы поливаются, пересаживаются в горшок

  • Овощи поливаются, собираются

Хорошо, а что если я сделаю что-то вроде следующего

class Plant{
  constructor(name){
   this.name = name
  }
 
 water(){
    console.log("Water the " + this.name)
 } 
}class Vegetable extends Plant {
  constructor(name, size, health){
   super(name)
   this.health = health;
  }  harvest(){
    console.log("Harvest the " + this.name)
  }
}class Flower extends Plant {
  constructor(name, size, health){
   super(name)   
   this.health = health;
  }  repot(){
    console.log( "Repot the " + this.name)
  }}class Fruit extends Plant {
  constructor(name, size, health){
   super(name)
   this.health = health;
  }
 
  repot(){
    console.log( "Repot the " + this.name)
  }  harvest(){
    console.log("Harvest the " + this.name)
  }
}

Это немного лучше, но теперь мы создаем дублирующие методы на разных экземплярах, которые делают одно и то же, что не соответствует принципам DRY (Don’t Repeat Yourself). Это проблема, которая может быть порождена паттерном наследования.

Проблема объектно-ориентированных языков в том, что они имеют всю эту неявную среду, которую переносят с собой. Вы хотели банан, а получили гориллу, которая держит банан и целые джунгли впридачу. - Джо Армстронг. Создатель Erlang.

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

Композиция

Здесь нам может помочь композиционный паттерн.

const harvest = () => {
  console.log("Harvesting")
}const water = () => {
  console.log("Watering)
}const repot = () => {
  console.log( "Repotting")
}const Flower = (name) => {
 return Object.assign(
  {name},
  water(),
  repot()
  )
}const Vegatable = (name) => {
 return Object.assign(
  {name},
  water(),
  harvest()
  )
}const Fruit = (name) => {
 return Object.assign(
  {name},
  water(),
  repot(),
  harvest()
  )
}const daffodil = Plant();
daffodil.harvest() // undefined
const banana = Fruit();
banana.harvest() // Harvesting

Отдавая предпочтение композиции перед наследованием и рассуждая с точки зрения того, что вещи делают, а не чем они являются, можно увидеть, что мы освободились от жестко связанной структуры наследования.

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

Можно заметить, что мы больше не полагаемся на прототипное наследование, а вместо этого используем функциональное инстанцирование для создания объекта. После инстанцирования переменная утрачивает связь с общими методами. Таким образом, никакие изменения не будут переданы экземплярам, инстанцированным до этого.

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

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

function Vegatable(name) {
  this.name = name return Object.assign(
    this,
    water(),
    harvest()
  )
}const Carrot = new Vegatable('Carrot')

В заключение

Композиция удобна, когда мы описываем отношения "имеет", в то время как наследование полезно при описании отношений "является".

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

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

.       .       .

Вот и все. Я надеюсь, что вы нашли это полезным и благодарю за чтение. Если эта статья понравилась и она оказалась интересной, вам также могут пригодиться некоторые из других идей, которые мы создали на !!!nerdy. Новые идеи появляются каждый месяц.


Материал подготовлен в рамках курса «JavaScript Developer. Professional». Если вам интересно узнать подробнее о формате обучения и программе, познакомиться с преподавателем курса — приглашаем на день открытых дверей онлайн. Регистрация здесь.

Теги:
Хабы:
Всего голосов 11: ↑5 и ↓6-1
Комментарии6

Публикации

Информация

Сайт
otus.ru
Дата регистрации
Дата основания
Численность
101–200 человек
Местоположение
Россия
Представитель
OTUS