В этом рассказе мы собираемся исследовать полиморфизм в спецификации JavaScript ES7.

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

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

А в правой части слайда вы можете увидеть другой альтернативный подход, который называется наследованием или композицией «HAS A». В этом случае наследующий класс (ClassB) не является экземпляром «super» (ClassA), но имеет свойство с экземпляром унаследованного класса.

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

Композиция («HAS A») дает другой способ гибкости в расширении класса, что означает, что лучше составить то, что может делать объект, чем расширять то, что он есть. Часто его можно использовать для инверсии управления и внедрения зависимостей. Типизированные языки с возможностью использования интерфейсов по-прежнему обеспечивают эффективный способ имитации принципа «IS A» в композиции. На данный момент JS не поддерживает интерфейсы, которые могут помочь сделать это более эффективным и понятным способом, но вы все же можете обернуть выполнение свойства «HAS A» в тот же интерфейс.

Наследование «IS A» дает возможность реализовать принцип полиморфизма. Попробуем описать, что это означает, несколькими определениями.

Полиморфизм в объектно-ориентированном программировании - это способность создавать свойство, функцию или объект, имеющий более одной реализации.

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

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

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

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

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

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

Для этого мы создадим родительский класс с именем Shape и, например, 3 дочерних класса: Circle, Rectangle и Triangle. , с помощью одного метода, который будет возвращать площадь формы на основе конфигураций формы. Кроме того, мы добавим код, который работает с экземплярами этих классов.

Итак, у нас есть три класса, которые расширяют базовый класс Shape и предоставляют различные реализации метода area. Кроме того, у нас есть метод cumulateShapes, который получает массив фигур и возвращает совокупное значение площади всех переданных фигур. Количество различных классов, реализующих класс Shape, не влияет на cumulateShapes; достаточно, чтобы каждый из этих классов поддерживал общий интерфейс, предоставляемый родительским классом.

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

Программа по-прежнему будет работать правильно, если мы передадим объект square в параметр массива cumulativeShape`s.

Как видите, реализация полиморфизма в JS ES7 часто может использовать другие важные принципы ООП, особенно наследование, но все же это отдельная концепция и может использоваться без наследования. Понимание этого вопроса позволяет вам предоставлять эффективные решения для связи между различными частями вашей системы, делает ваш код более чистым и читаемым. Чтобы понять это глубже и улучшить использование полиморфизма, я рекомендую вам ознакомиться с принципами подстановки Лискова и SOLID разделения интерфейсов. Вы легко найдете об этом достаточно информации.
В следующей статье речь идет о инкапсуляции.