Полиморфизм
9 дней назад
Nikolai Gagarinov
Ответы
Полиморфизм
Слово полиморфизм происходит от греческого πολύμορφος — «многообразный, имеющий множество форм». В программировании это означает способность одной и той же команды, функции или метода вести себя по-разному в зависимости от типа данных, с которыми он работает.
Пример на Python:
Функция animal_sound() не знает, кто перед ней — Dog или Cat, но работает одинаково с обоими. Это и есть полиморфизм.
Принципы ООП

Полиморфизм — один из четырех базовых принципов объектно-ориентированного программирования (ООП) наряду с:
-
Инкапсуляцией — сокрытием деталей реализации.
-
Наследованием — механизмом повторного использования кода.
-
Абстракцией — выделением значимых свойств объекта.
Эти принципы связаны логически. Наследование создает иерархию классов, инкапсуляция прячет детали, абстракция задает общие контракты, а полиморфизм позволяет взаимодействовать с объектами по этому контракту, не заботясь об их типе.
Принцип подстановки Лисков (LSP)
Полиморфизм подтипов невозможен без соблюдения принципа подстановки Лисков (Liskov Substitution Principle). Барбара Лисков сформулировала его в 1987 году так:
Если S — подтип T, то объекты типа T в программе можно заменить объектами типа S без нарушения корректности.
Иными словами, дочерний класс должен сохранять поведение базового, а не ломать ожидания клиента.
Пример нарушения LSP:
Square нарушает ожидания клиента, потому что меняет семантику методов базового класса. Формально тип совместим, но поведение — нет. Это антипример корректного полиморфизма.
Виды полиморфизма
Полиморфизм бывает разных видов. Классификация зависит от момента связывания (compile-time / run-time) и способа выражения.
Полиморфизм подтипов

Самый классический вид, реализуемый через наследование и интерфейсы.
void render(Drawable d) { d.draw(); }
Метод render() работает с любым объектом, реализующим интерфейс Drawable. Такой подход облегчает расширение API — можно добавить Triangle, не изменяя render().
Параметрический полиморфизм
Характерен для обобщённого программирования — Generics в Java, C# или Templates в C++.
Функция становится универсальной для разных типов.
Код компилируется отдельно для каждого конкретного типа — так работает compile-time полиморфизм.
Ad hoc-полиморфизм
Это перегрузка функций и операторов, когда несколько реализаций имеют одно имя, но разные сигнатуры.
Такой полиморфизм тоже «многоформен», но решается на этапе компиляции.
Статический и динамический полиморфизм

Статический (compile-time) реализуется с помощью перегрузки функций, операторов и шаблонов. Все решения принимаются на этапе компиляции, что делает код быстрым, но менее гибким.
Динамический (run-time) работает через виртуальные функции и vtable. Он позволяет выбирать нужную реализацию во время выполнения программы.
Пример на C++
class Shape {
Здесь используется динамическая диспетчеризация через таблицу виртуальных функций (vtable).
Цена вызова: динамический полиморфизм чуть медленнее (1–2 % накладных расходов), но современные компиляторы часто инлайнят виртуальные вызовы при предсказуемом типе.
Полиморфизм в популярных языках программирования
C++
-
virtual,override— основа динамического полиморфизма. -
templates— параметрический вариант. -
CRTP(Curiously Recurring Template Pattern) — шаблон, позволяющий имитировать виртуальные вызовы на этапе компиляции.
Java
-
Поддерживает динамический полиморфизм через
abstractиinterface. -
Перегрузка (
overloading) работает статически. -
Genericsреализованы через type erasure — типы стираются при компиляции, оставляя единый байт-код.
C#
-
Generics более мощные: типовая информация сохраняется во время выполнения (reified generics).
-
virtual,overrideобеспечивают полиморфизм подтипов. -
dynamic— отдельный механизм позднего связывания.
Python и JavaScript
-
Основываются на duck typing — «если объект крякает как утка, значит, он утка».
-
Нет формальных интерфейсов (в Python — появились
abcиtyping.Protocol). -
Механизм гибкий, но ошибки проявляются только в рантайме.
C
- В классическом C нет ООП, но полиморфизм можно реализовать вручную:
Структура с указателем на функцию — ручная версия виртуального метода.
Практическая реализация

Overriding vs Overloading vs Operator Overloading
Типичная ошибка — путать overriding и overloading, ожидая полиморфизма, где его нет.
Интерфейсы против абстрактных классов
-
Интерфейс определяет контракт (только сигнатуры методов).
-
Абстрактный класс может содержать реализацию.
-
Если нужно описать, что делает объект — используйте интерфейс, если как делает — абстрактный класс.
Композиция против наследования
«Предпочитай композицию наследованию» (GoF)
Композиция позволяет гибко собирать объекты, избегая жестких иерархий. Например, класс Car может содержать Engine, а не наследоваться от него.
Расширяемость API: фабрики, стратегии, плагины
Полиморфизм лежит в основе паттернов:
-
Фабричный метод — создает экземпляры разных подклассов по единому интерфейсу.
-
Стратегия — позволяет подменять алгоритм на лету.
-
Плагин-архитектура — динамическое подключение модулей.
Преимущества и ограничения
Плюсы:
-
Унификация интерфейсов, читаемость кода.
-
Возможность расширять функциональность без изменения существующего кода (Open-Closed Principle).
-
Повторное использование, модульность.
Минусы:
-
Усложнение архитектуры при чрезмерных иерархиях.
-
Потери производительности (незначительные, но есть).
-
Затрудненная отладка в глубоко наследуемых структурах.
Практический совет: начинайте с композиции и интерфейсов, добавляйте наследование только при явной необходимости.
Частые ошибки, анти-паттерны

-
Глубокие иерархии — трудно сопровождать.
-
Нарушение LSP — подтип ломает ожидания клиента.
-
Смешение overloading и overriding — методы не переопределяются, а перегружаются по ошибке.
-
Преждевременная абстракция — добавление интерфейсов «на будущее».
Лучше немного дублировать код, чем создавать абстракцию, которую никто не использует.
FAQ
Что такое полиморфизм в программировании простыми словами? Это способность функции или метода вести себя по-разному в зависимости от типа объекта, с которым он работает.
Чем отличается статический полиморфизм от динамического? Статический определяется во время компиляции (overload, templates), а динамический — во время выполнения (virtual methods, duck typing).
Что такое принцип подстановки Лисков? Это правило, требующее, чтобы объект подкласса можно было использовать вместо базового без изменения поведения программы.
Можно ли реализовать полиморфизм в C? Да, вручную через структуры с указателями на функции — аналог виртуальных методов.
Глоссарий
-
Подтип — тип, совместимый с базовым, удовлетворяющий LSP.
-
Интерфейс — набор сигнатур методов без реализации.
-
Абстракция — выделение существенных свойств.
-
Перегрузка (overloading) — одинаковое имя, разные параметры.
-
Переопределение (overriding) — новая реализация метода базового класса.
-
Vtable — таблица виртуальных функций, используемая для диспетчеризации вызовов.
-
Диспетчеризация — выбор нужной реализации метода.
-
Generics/Templates — механизм параметрического полиморфизма.
-
Duck typing — поведение определяется набором методов, а не типом.
Ссылки и источники
-
Barbara Liskov, "Data Abstraction and Hierarchy" (1987)
-
Design Patterns: Elements of Reusable Object-Oriented Software — GoF
-
Effective Java — Joshua Bloch
-
C++ Templates: The Complete Guide — David Vandevoorde
-
cppreference.com — документация по C++
-
docs.oracle.com/javase/tutorial/java/IandI/polymorphism.html
9 дней назад
Nikolai Gagarinov


.png)

.png)
