Полиморфизм

Возможность работать с разными объектами через один общий интерфейс: код вызывает метод, а что именно произойдет, решает сам объект.

Полиморфизм (от греческого «многообразие форм») — принцип, при котором один и тот же вызов работает с объектами разных типов, и каждый объект выполняет его по-своему. Код оформления заказа просто говорит «оплатить», а как именно пройдет оплата — картой, через СБП или по счету, — решает выбранный способ оплаты.

Пример

// Kotlin
interface PaymentMethod {
    fun pay(amount: Long)
}

class CardPayment : PaymentMethod {
    override fun pay(amount: Long) { /* списание с банковской карты */ }
}

class SbpPayment : PaymentMethod {
    override fun pay(amount: Long) { /* перевод через СБП */ }
}

fun checkout(method: PaymentMethod, amount: Long) = method.pay(amount)

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

Виды полиморфизма

  • Через общий интерфейс или наследование — как в примере выше: разные классы реализуют один интерфейс. Это то, что обычно имеют в виду в ООП.
  • Параметрический — один код работает с любым типом данных: список List<T> одинаково хранит и строки, и заказы. Это обобщенные типы (generics).
  • Специальный — перегрузка: несколько функций с одним именем для разных типов аргументов, например сложение чисел и сложение строк.

Зачем это нужно

  • Расширение без переделки. Новое поведение добавляется новым классом, а работающий код не трогают. Это один из принципов хорошего проектирования: код открыт для расширения, но закрыт для изменения.
  • Меньше условий. Вместо длинных цепочек «если карта — то так, если СБП — то так» в одном месте каждый вариант живет в своем классе.
  • Проще тестировать. В тестах вместо настоящей оплаты подставляют тестовую реализацию того же интерфейса.

Связь с другими понятиями

Полиморфизм работает в паре с инкапсуляцией: объект скрывает, как он выполняет действие, а полиморфизм позволяет вызывать это действие одинаково для всех. На нем построены многие шаблоны проектирования — Стратегия, Команда, Наблюдатель. А в языках с динамической типизацией, например Python, полиморфизм работает и без общих интерфейсов: достаточно, чтобы у объекта был нужный метод («утиная типизация»).