- Что такое полиморфизм?
- На практике
- Виды полиморфизма
- Параметрический полиморфизм
- Ad-hoc полиморфизм
- Полиморфизм подтипов
- Итоги
Корректировка к слайду «Параметрический полиморфизм»:
append(numbers1, numbers2)
append(strings1, strings2)
--
В этом уроке мы поговорим немножко о терминах и о такой вещи, как полиморфизм.
Что такое полиморфизм?
В языках программирования и теории типов полиморфизмом
называется способность функции обрабатывать данные разных типов.
На практике
Полиморфизм повышает коэффициент повторного использования кода,
то есть уменьшает дублирование.
Виды полиморфизма
- Ad-hoc (Специальный)
- Параметрический
- Подтипов (включения)
Со всеми примерами этих полиморфизмов мы уже встречались, но просто никогда не использовали эти термины.
Параметрический полиморфизм
Вызов ОДНОГО и того же кода для ВСЕХ
допустимых типов (полиморфных) аргументов.
Параметрический полиморфизм в теории типов и у людей, которые часто используют функциональные языки, обычно принято называть словом полиморфизм, что кстати говоря идёт в разрез с тем, что принято у программистов, которые пишут исключительно на объектно-ориентированных языках. Они под словом полиморфизм понимают полиморфизм подтипов.
Давайте представим ситуацию, с которой мы уже сталкивались. У нас есть список и мы вызываем операцию «собрать два списка в один»:
const numbers1 = l(3, 4, 5, 8)
const numbers2 = l(3, 2, 9)
append(numbers1, numbers2) // (3, 4, 5, 8, 2, 9)
append
берет два списка и делает из них один.
Ещё один пример той же самой операции, но разница в том, что теперь здесь список строк:
const strings1 = l('cat', 'dog')
const strings2 = l('table', 'milk', 'phone')
append(strings1, strings2) // (cat, dog, table, milk, phone)
Поскольку мы работаем в динамическом языке нам без разницы с какими типами работать. Какой бы список у нас не был и какой бы тип не содержался внутри какого-то основного типа – всё будет работать.
В статических языках это совершенно не так и вы не можете просто так определить список чего угодно. У вас будет список конкретных структур, поэтому в таких языках нужны специальные механизмы, которые позволяют определять обобщённые списки.
Это важно по той простой причине, что применяя функцию append
(операция объединения списков) можно увидеть, что над элементами, которые лежат в списке, никаких действий не производится вообще. Они не участвуют в этой операции и им не нужно это. То есть объединяя два списка мы не делаем никаких преобразований внутри. Соответственно, эта операция в общем случае не зависит от того, что хранится внутри.
Идеально – язык программирования должен позволять нам работать с этим именно так.
Как мы уже говорили, в динамических языках мы получаем параметрический полиморфизм из коробки и действительно – функция append
одна для любых списков, потому что мы не делаем здесь никаких различий, код будет один и его будет довольно мало.
В статических языках нет механизма для поддержки параметрического полиморфизма, им придётся писать функцию append
для каждого типа. Представьте себе сколько может быть списков и сколько вообще может быть таких структур, для которых придётся так делать. По этой причине там существуют специальные механизмы.
Особенно эти проблемы существуют в объектно-ориентированных языках, потому что в функциональных языках параметрический полиморфизм есть всегда и из коробки – это основа, а вот например, в таких языках как Java, существует специальный механизм, который называется дженерики. В C++ существует механизм, который называется темплейты.
В каких-то других языках это может называться по-другому, но всё это сводится к тому, чтобы иметь возможность пользоваться параметрическим полиморфизмом у себя в языке.
Ad-hoc полиморфизм
Это самый простой тип полиморфизма. Он связан с тем, что в зависимости от типов аргументов применяется разная реализация какой-то операции.
Например, в данном случае сложения:
1 + 1 // 2
'cat' + 'dog' // catdog
console.log(1034.98) // => 1034.98
console.log('hello') // => hello
Если у нас типы число и число, то у нас будет арифметическая операция. Если у нас две строчки, то это будет конкатенация. Если будут разные типы, то там уже зависит от ситуации.
Причём в статических языках, опять же, ad-hoc полиморфизм реализуется за счёт перегрузки функций (то есть описываются разные тела функций, в которых задаются разные типы аргументов) и соответственно компилятор сам выбирает какую функцию в какой момент использовать. Внутри нет никаких if’ов, то есть мы уже работаем конкретно с теми типами, которые мы определили в данной функции. Под одним именем одной операции скрывается несколько разных тел.
В динамических языках мы этого делать не можем, поэтому там по сути всегда это сводится к тому, что у вас есть одна функция, внутри которой много if’ов, которые проверяют типы аргументов.
Например, если посмотреть исходники console.log
, можно увидеть, что там это примерно так и устроено. Там используется функция формат, которая внутри проверяет типы аргументов и в зависимости от типа делает какие-то разные действия.
Полиморфизм подтипов
Напомню, что полиморфизм подтипов в объектно-ориентированных языках называют просто полиморфизм, что не совсем корректно.
Как раз этот полиморфизм позволяет вызывать РАЗНЫЙ код для РАЗНЫХ иерархий типов.
const obj = new SimpleCard() // or PercentCard()
obj.damage(health)
Собственно, с этим полиморфизмом мы сквозь весь наш курс в первую очередь и знакомились и использовали его в нашей программе. И именно задача нашего курса была завести внутри языка полиморфизм подтипов.
Полиморфизм подтипов является ключевой штукой для ООП.
Почему это нужно – мы уже прекрасно знаем. Бывает, что для разных типов нужно вызывать разную реализацию одних и тех же (с точки зрения интерфейса) функций для того, чтобы не писать if
.
Это как раз к вопросу о том, что эта штука исключительно просто сокращает код с точки зрения практики, а формально вы, конечно, можете работать и без него.
Итоги
Полиморфизм связан с динамической диспетчеризацией, которую мы разбирали. Просто динамическая диспетчеризация – это один из способов реализации полиморфизма.
Любой язык и любая штука, с которой мы работаем, позволяет нам использовать полиморфизм и как раз с помощью динамической диспетчеризации мы можем его, собственно, реализовать и таким образом просто сокращать тот код, который мы пишем.
Для полного доступа к курсу нужен базовый план
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.