0 added
0 removed
Original
2026-01-01
Modified
2026-02-26
1
<h2>Полиморфизм</h2>
1
<h2>Полиморфизм</h2>
2
<p>Слово<strong>полиморфизм</strong>происходит от греческого<em>πολύμορφος</em>- "многообразный, имеющий множество форм". В программировании это означает способность<strong>одной и той же команды, функции или метода вести себя по-разному</strong>в зависимости от типа данных, с которыми он работает.</p>
2
<p>Слово<strong>полиморфизм</strong>происходит от греческого<em>πολύμορφος</em>- "многообразный, имеющий множество форм". В программировании это означает способность<strong>одной и той же команды, функции или метода вести себя по-разному</strong>в зависимости от типа данных, с которыми он работает.</p>
3
<p>Пример на Python:</p>
3
<p>Пример на Python:</p>
4
<p>Функция animal_sound() не знает, кто перед ней - Dog или Cat, но работает одинаково с обоими. Это и есть<strong>полиморфизм</strong>.</p>
4
<p>Функция animal_sound() не знает, кто перед ней - Dog или Cat, но работает одинаково с обоими. Это и есть<strong>полиморфизм</strong>.</p>
5
<h2>Принципы ООП</h2>
5
<h2>Принципы ООП</h2>
6
<p>Полиморфизм - один из<strong>четырех базовых принципов объектно-ориентированного программирования (ООП)</strong>наряду с:</p>
6
<p>Полиморфизм - один из<strong>четырех базовых принципов объектно-ориентированного программирования (ООП)</strong>наряду с:</p>
7
<ol><li><p><strong>Инкапсуляцией</strong>- сокрытием деталей реализации.</p>
7
<ol><li><p><strong>Инкапсуляцией</strong>- сокрытием деталей реализации.</p>
8
</li>
8
</li>
9
<li><p><strong>Наследованием</strong>- механизмом повторного использования кода.</p>
9
<li><p><strong>Наследованием</strong>- механизмом повторного использования кода.</p>
10
</li>
10
</li>
11
<li><p><strong>Абстракцией</strong>- выделением значимых свойств объекта.</p>
11
<li><p><strong>Абстракцией</strong>- выделением значимых свойств объекта.</p>
12
</li>
12
</li>
13
</ol><p>Эти принципы связаны логически. Наследование создает иерархию классов, инкапсуляция прячет детали, абстракция задает общие контракты, а полиморфизм позволяет<strong>взаимодействовать с объектами по этому контракту</strong>, не заботясь об их типе.</p>
13
</ol><p>Эти принципы связаны логически. Наследование создает иерархию классов, инкапсуляция прячет детали, абстракция задает общие контракты, а полиморфизм позволяет<strong>взаимодействовать с объектами по этому контракту</strong>, не заботясь об их типе.</p>
14
<h3>Принцип подстановки Лисков (LSP)</h3>
14
<h3>Принцип подстановки Лисков (LSP)</h3>
15
<p>Полиморфизм подтипов невозможен без соблюдения<strong>принципа подстановки Лисков (Liskov Substitution Principle)</strong>. Барбара Лисков сформулировала его в 1987 году так:</p>
15
<p>Полиморфизм подтипов невозможен без соблюдения<strong>принципа подстановки Лисков (Liskov Substitution Principle)</strong>. Барбара Лисков сформулировала его в 1987 году так:</p>
16
<p>Если S - подтип T, то объекты типа T в программе можно заменить объектами типа S без нарушения корректности.</p>
16
<p>Если S - подтип T, то объекты типа T в программе можно заменить объектами типа S без нарушения корректности.</p>
17
<p>Иными словами, дочерний класс<strong>должен сохранять поведение базового</strong>, а не ломать ожидания клиента.</p>
17
<p>Иными словами, дочерний класс<strong>должен сохранять поведение базового</strong>, а не ломать ожидания клиента.</p>
18
<p><strong>Пример нарушения LSP:</strong></p>
18
<p><strong>Пример нарушения LSP:</strong></p>
19
<p>Square нарушает ожидания клиента, потому что меняет семантику методов базового класса. Формально тип совместим, но поведение - нет. Это антипример корректного полиморфизма.</p>
19
<p>Square нарушает ожидания клиента, потому что меняет семантику методов базового класса. Формально тип совместим, но поведение - нет. Это антипример корректного полиморфизма.</p>
20
<h2>Виды полиморфизма</h2>
20
<h2>Виды полиморфизма</h2>
21
<p>Полиморфизм бывает<strong>разных видов</strong>. Классификация зависит от момента связывания (compile-time / run-time) и способа выражения.</p>
21
<p>Полиморфизм бывает<strong>разных видов</strong>. Классификация зависит от момента связывания (compile-time / run-time) и способа выражения.</p>
22
<h3>Полиморфизм подтипов</h3>
22
<h3>Полиморфизм подтипов</h3>
23
<p>Самый классический вид, реализуемый через<strong>наследование</strong>и<strong>интерфейсы</strong>.</p>
23
<p>Самый классический вид, реализуемый через<strong>наследование</strong>и<strong>интерфейсы</strong>.</p>
24
<p>void render(Drawable d) { d.draw(); }</p>
24
<p>void render(Drawable d) { d.draw(); }</p>
25
<p>Метод render() работает с любым объектом, реализующим интерфейс Drawable. Такой подход облегчает расширение API - можно добавить Triangle, не изменяя render().</p>
25
<p>Метод render() работает с любым объектом, реализующим интерфейс Drawable. Такой подход облегчает расширение API - можно добавить Triangle, не изменяя render().</p>
26
<h3>Параметрический полиморфизм</h3>
26
<h3>Параметрический полиморфизм</h3>
27
<p>Характерен для обобщённого программирования -<strong>Generics в Java, C#</strong>или<strong>Templates в C++</strong>. Функция становится универсальной для разных типов.</p>
27
<p>Характерен для обобщённого программирования -<strong>Generics в Java, C#</strong>или<strong>Templates в C++</strong>. Функция становится универсальной для разных типов.</p>
28
<p>Код компилируется отдельно для каждого конкретного типа - так работает<strong>compile-time полиморфизм</strong>.</p>
28
<p>Код компилируется отдельно для каждого конкретного типа - так работает<strong>compile-time полиморфизм</strong>.</p>
29
<h3>Ad hoc-полиморфизм</h3>
29
<h3>Ad hoc-полиморфизм</h3>
30
<p>Это<strong>перегрузка функций и операторов</strong>, когда несколько реализаций имеют одно имя, но разные сигнатуры.</p>
30
<p>Это<strong>перегрузка функций и операторов</strong>, когда несколько реализаций имеют одно имя, но разные сигнатуры.</p>
31
<p>Такой полиморфизм тоже "многоформен", но решается<strong>на этапе компиляции</strong>.</p>
31
<p>Такой полиморфизм тоже "многоформен", но решается<strong>на этапе компиляции</strong>.</p>
32
<h2>Статический и динамический полиморфизм</h2>
32
<h2>Статический и динамический полиморфизм</h2>
33
<p><strong>Статический (compile-time)</strong>реализуется с помощью перегрузки функций, операторов и шаблонов. Все решения принимаются на этапе компиляции, что делает код быстрым, но менее гибким.</p>
33
<p><strong>Статический (compile-time)</strong>реализуется с помощью перегрузки функций, операторов и шаблонов. Все решения принимаются на этапе компиляции, что делает код быстрым, но менее гибким.</p>
34
<p><strong>Динамический (run-time)</strong>работает через<strong>виртуальные функции</strong>и<strong>vtable</strong>. Он позволяет выбирать нужную реализацию<strong>во время выполнения программы</strong>.</p>
34
<p><strong>Динамический (run-time)</strong>работает через<strong>виртуальные функции</strong>и<strong>vtable</strong>. Он позволяет выбирать нужную реализацию<strong>во время выполнения программы</strong>.</p>
35
<h3>Пример на C++</h3>
35
<h3>Пример на C++</h3>
36
<p>class Shape {</p>
36
<p>class Shape {</p>
37
<p>Здесь используется<strong>динамическая диспетчеризация</strong>через таблицу виртуальных функций (vtable).</p>
37
<p>Здесь используется<strong>динамическая диспетчеризация</strong>через таблицу виртуальных функций (vtable).</p>
38
<p><strong>Цена вызова:</strong>динамический полиморфизм чуть медленнее (1-2 % накладных расходов), но современные компиляторы часто<strong>инлайнят</strong>виртуальные вызовы при предсказуемом типе.</p>
38
<p><strong>Цена вызова:</strong>динамический полиморфизм чуть медленнее (1-2 % накладных расходов), но современные компиляторы часто<strong>инлайнят</strong>виртуальные вызовы при предсказуемом типе.</p>
39
<h2>Полиморфизм в популярных языках программирования</h2>
39
<h2>Полиморфизм в популярных языках программирования</h2>
40
<h3>C++</h3>
40
<h3>C++</h3>
41
<ul><li><p>virtual, override - основа динамического полиморфизма.</p>
41
<ul><li><p>virtual, override - основа динамического полиморфизма.</p>
42
</li>
42
</li>
43
<li><p>templates - параметрический вариант.</p>
43
<li><p>templates - параметрический вариант.</p>
44
</li>
44
</li>
45
<li><p>CRTP (Curiously Recurring Template Pattern) - шаблон, позволяющий имитировать виртуальные вызовы на этапе компиляции.</p>
45
<li><p>CRTP (Curiously Recurring Template Pattern) - шаблон, позволяющий имитировать виртуальные вызовы на этапе компиляции.</p>
46
</li>
46
</li>
47
</ul><h3>Java</h3>
47
</ul><h3>Java</h3>
48
<ul><li><p>Поддерживает<strong>динамический полиморфизм</strong>через abstract и interface.</p>
48
<ul><li><p>Поддерживает<strong>динамический полиморфизм</strong>через abstract и interface.</p>
49
</li>
49
</li>
50
<li><p>Перегрузка (overloading) работает статически.</p>
50
<li><p>Перегрузка (overloading) работает статически.</p>
51
</li>
51
</li>
52
<li><p>Generics реализованы через<strong>type erasure</strong>- типы стираются при компиляции, оставляя единый байт-код.</p>
52
<li><p>Generics реализованы через<strong>type erasure</strong>- типы стираются при компиляции, оставляя единый байт-код.</p>
53
</li>
53
</li>
54
</ul><h3>C#</h3>
54
</ul><h3>C#</h3>
55
<ul><li><p>Generics более мощные: типовая информация сохраняется во время выполнения (<strong>reified generics</strong>).</p>
55
<ul><li><p>Generics более мощные: типовая информация сохраняется во время выполнения (<strong>reified generics</strong>).</p>
56
</li>
56
</li>
57
<li><p>virtual, override обеспечивают полиморфизм подтипов.</p>
57
<li><p>virtual, override обеспечивают полиморфизм подтипов.</p>
58
</li>
58
</li>
59
<li><p>dynamic - отдельный механизм позднего связывания.</p>
59
<li><p>dynamic - отдельный механизм позднего связывания.</p>
60
</li>
60
</li>
61
</ul><h3>Python и JavaScript</h3>
61
</ul><h3>Python и JavaScript</h3>
62
<ul><li><p>Основываются на<strong>duck typing</strong>- "если объект крякает как утка, значит, он утка".</p>
62
<ul><li><p>Основываются на<strong>duck typing</strong>- "если объект крякает как утка, значит, он утка".</p>
63
</li>
63
</li>
64
<li><p>Нет формальных интерфейсов (в Python - появились abc и typing.Protocol).</p>
64
<li><p>Нет формальных интерфейсов (в Python - появились abc и typing.Protocol).</p>
65
</li>
65
</li>
66
<li><p>Механизм гибкий, но ошибки проявляются только в рантайме.</p>
66
<li><p>Механизм гибкий, но ошибки проявляются только в рантайме.</p>
67
</li>
67
</li>
68
</ul><h3>C</h3>
68
</ul><h3>C</h3>
69
<ul><li>В классическом C нет ООП, но полиморфизм можно реализовать вручную:</li>
69
<ul><li>В классическом C нет ООП, но полиморфизм можно реализовать вручную:</li>
70
</ul><p>Структура с указателем на функцию - ручная версия виртуального метода.</p>
70
</ul><p>Структура с указателем на функцию - ручная версия виртуального метода.</p>
71
<h2>Практическая реализация</h2>
71
<h2>Практическая реализация</h2>
72
<h3>Overriding vs Overloading vs Operator Overloading</h3>
72
<h3>Overriding vs Overloading vs Operator Overloading</h3>
73
<p>Типичная ошибка - путать overriding и overloading, ожидая полиморфизма, где его нет.</p>
73
<p>Типичная ошибка - путать overriding и overloading, ожидая полиморфизма, где его нет.</p>
74
<h3>Интерфейсы против абстрактных классов</h3>
74
<h3>Интерфейсы против абстрактных классов</h3>
75
<ul><li><p><strong>Интерфейс</strong>определяет контракт (только сигнатуры методов).</p>
75
<ul><li><p><strong>Интерфейс</strong>определяет контракт (только сигнатуры методов).</p>
76
</li>
76
</li>
77
<li><p><strong>Абстрактный класс</strong>может содержать реализацию.</p>
77
<li><p><strong>Абстрактный класс</strong>может содержать реализацию.</p>
78
</li>
78
</li>
79
<li><p>Если нужно описать,<em>что делает объект</em>- используйте интерфейс, если<em>как делает</em>- абстрактный класс.</p>
79
<li><p>Если нужно описать,<em>что делает объект</em>- используйте интерфейс, если<em>как делает</em>- абстрактный класс.</p>
80
</li>
80
</li>
81
</ul><h3>Композиция против наследования</h3>
81
</ul><h3>Композиция против наследования</h3>
82
<p>"Предпочитай композицию наследованию" (GoF)</p>
82
<p>"Предпочитай композицию наследованию" (GoF)</p>
83
<p>Композиция позволяет гибко собирать объекты, избегая жестких иерархий. Например, класс Car может содержать Engine, а не наследоваться от него.</p>
83
<p>Композиция позволяет гибко собирать объекты, избегая жестких иерархий. Например, класс Car может содержать Engine, а не наследоваться от него.</p>
84
<h3>Расширяемость API: фабрики, стратегии, плагины</h3>
84
<h3>Расширяемость API: фабрики, стратегии, плагины</h3>
85
<p>Полиморфизм лежит в основе паттернов:</p>
85
<p>Полиморфизм лежит в основе паттернов:</p>
86
<ul><li><p><strong>Фабричный метод</strong>- создает экземпляры разных подклассов по единому интерфейсу.</p>
86
<ul><li><p><strong>Фабричный метод</strong>- создает экземпляры разных подклассов по единому интерфейсу.</p>
87
</li>
87
</li>
88
<li><p><strong>Стратегия</strong>- позволяет подменять алгоритм на лету.</p>
88
<li><p><strong>Стратегия</strong>- позволяет подменять алгоритм на лету.</p>
89
</li>
89
</li>
90
<li><p><strong>Плагин-архитектура</strong>- динамическое подключение модулей.</p>
90
<li><p><strong>Плагин-архитектура</strong>- динамическое подключение модулей.</p>
91
</li>
91
</li>
92
</ul><h2>Преимущества и ограничения</h2>
92
</ul><h2>Преимущества и ограничения</h2>
93
<p><strong>Плюсы:</strong></p>
93
<p><strong>Плюсы:</strong></p>
94
<ul><li><p>Унификация интерфейсов, читаемость кода.</p>
94
<ul><li><p>Унификация интерфейсов, читаемость кода.</p>
95
</li>
95
</li>
96
<li><p>Возможность расширять функциональность без изменения существующего кода (Open-Closed Principle).</p>
96
<li><p>Возможность расширять функциональность без изменения существующего кода (Open-Closed Principle).</p>
97
</li>
97
</li>
98
<li><p>Повторное использование, модульность.</p>
98
<li><p>Повторное использование, модульность.</p>
99
</li>
99
</li>
100
</ul><p><strong>Минусы:</strong></p>
100
</ul><p><strong>Минусы:</strong></p>
101
<ul><li><p>Усложнение архитектуры при чрезмерных иерархиях.</p>
101
<ul><li><p>Усложнение архитектуры при чрезмерных иерархиях.</p>
102
</li>
102
</li>
103
<li><p>Потери производительности (незначительные, но есть).</p>
103
<li><p>Потери производительности (незначительные, но есть).</p>
104
</li>
104
</li>
105
<li><p>Затрудненная отладка в глубоко наследуемых структурах.</p>
105
<li><p>Затрудненная отладка в глубоко наследуемых структурах.</p>
106
</li>
106
</li>
107
</ul><p>Практический совет: начинайте с композиции и интерфейсов, добавляйте наследование только при явной необходимости.</p>
107
</ul><p>Практический совет: начинайте с композиции и интерфейсов, добавляйте наследование только при явной необходимости.</p>
108
<h2>Частые ошибки, анти-паттерны</h2>
108
<h2>Частые ошибки, анти-паттерны</h2>
109
<ul><li><p><strong>Глубокие иерархии</strong>- трудно сопровождать.</p>
109
<ul><li><p><strong>Глубокие иерархии</strong>- трудно сопровождать.</p>
110
</li>
110
</li>
111
<li><p><strong>Нарушение LSP</strong>- подтип ломает ожидания клиента.</p>
111
<li><p><strong>Нарушение LSP</strong>- подтип ломает ожидания клиента.</p>
112
</li>
112
</li>
113
<li><p><strong>Смешение overloading и overriding</strong>- методы не переопределяются, а перегружаются по ошибке.</p>
113
<li><p><strong>Смешение overloading и overriding</strong>- методы не переопределяются, а перегружаются по ошибке.</p>
114
</li>
114
</li>
115
<li><p><strong>Преждевременная абстракция</strong>- добавление интерфейсов "на будущее".</p>
115
<li><p><strong>Преждевременная абстракция</strong>- добавление интерфейсов "на будущее".</p>
116
</li>
116
</li>
117
</ul><p>Лучше немного дублировать код, чем создавать абстракцию, которую никто не использует.</p>
117
</ul><p>Лучше немного дублировать код, чем создавать абстракцию, которую никто не использует.</p>
118
<h2>FAQ</h2>
118
<h2>FAQ</h2>
119
<p><strong>Что такое полиморфизм в программировании простыми словами?</strong>Это способность функции или метода вести себя по-разному в зависимости от типа объекта, с которым он работает.</p>
119
<p><strong>Что такое полиморфизм в программировании простыми словами?</strong>Это способность функции или метода вести себя по-разному в зависимости от типа объекта, с которым он работает.</p>
120
<p><strong>Чем отличается статический полиморфизм от динамического?</strong>Статический определяется во время компиляции (overload, templates), а динамический - во время выполнения (virtual methods, duck typing).</p>
120
<p><strong>Чем отличается статический полиморфизм от динамического?</strong>Статический определяется во время компиляции (overload, templates), а динамический - во время выполнения (virtual methods, duck typing).</p>
121
<p><strong>Что такое принцип подстановки Лисков?</strong>Это правило, требующее, чтобы объект подкласса можно было использовать вместо базового без изменения поведения программы.</p>
121
<p><strong>Что такое принцип подстановки Лисков?</strong>Это правило, требующее, чтобы объект подкласса можно было использовать вместо базового без изменения поведения программы.</p>
122
<p><strong>Можно ли реализовать полиморфизм в C?</strong>Да, вручную через структуры с указателями на функции - аналог виртуальных методов.</p>
122
<p><strong>Можно ли реализовать полиморфизм в C?</strong>Да, вручную через структуры с указателями на функции - аналог виртуальных методов.</p>
123
<h2>Глоссарий</h2>
123
<h2>Глоссарий</h2>
124
<ul><li><p><strong>Подтип</strong>- тип, совместимый с базовым, удовлетворяющий LSP.</p>
124
<ul><li><p><strong>Подтип</strong>- тип, совместимый с базовым, удовлетворяющий LSP.</p>
125
</li>
125
</li>
126
<li><p><strong>Интерфейс</strong>- набор сигнатур методов без реализации.</p>
126
<li><p><strong>Интерфейс</strong>- набор сигнатур методов без реализации.</p>
127
</li>
127
</li>
128
<li><p><strong>Абстракция</strong>- выделение существенных свойств.</p>
128
<li><p><strong>Абстракция</strong>- выделение существенных свойств.</p>
129
</li>
129
</li>
130
<li><p><strong>Перегрузка (overloading)</strong>- одинаковое имя, разные параметры.</p>
130
<li><p><strong>Перегрузка (overloading)</strong>- одинаковое имя, разные параметры.</p>
131
</li>
131
</li>
132
<li><p><strong>Переопределение (overriding)</strong>- новая реализация метода базового класса.</p>
132
<li><p><strong>Переопределение (overriding)</strong>- новая реализация метода базового класса.</p>
133
</li>
133
</li>
134
<li><p><strong>Vtable</strong>- таблица виртуальных функций, используемая для диспетчеризации вызовов.</p>
134
<li><p><strong>Vtable</strong>- таблица виртуальных функций, используемая для диспетчеризации вызовов.</p>
135
</li>
135
</li>
136
<li><p><strong>Диспетчеризация</strong>- выбор нужной реализации метода.</p>
136
<li><p><strong>Диспетчеризация</strong>- выбор нужной реализации метода.</p>
137
</li>
137
</li>
138
<li><p><strong>Generics/Templates</strong>- механизм параметрического полиморфизма.</p>
138
<li><p><strong>Generics/Templates</strong>- механизм параметрического полиморфизма.</p>
139
</li>
139
</li>
140
<li><p><strong>Duck typing</strong>- поведение определяется набором методов, а не типом.</p>
140
<li><p><strong>Duck typing</strong>- поведение определяется набором методов, а не типом.</p>
141
</li>
141
</li>
142
</ul><h2>Ссылки и источники</h2>
142
</ul><h2>Ссылки и источники</h2>
143
<ul><li><p><em>Barbara Liskov, "Data Abstraction and Hierarchy" (1987)</em></p>
143
<ul><li><p><em>Barbara Liskov, "Data Abstraction and Hierarchy" (1987)</em></p>
144
</li>
144
</li>
145
<li><p><em>Design Patterns: Elements of Reusable Object-Oriented Software</em>- GoF</p>
145
<li><p><em>Design Patterns: Elements of Reusable Object-Oriented Software</em>- GoF</p>
146
</li>
146
</li>
147
<li><p><em>Effective Java</em>- Joshua Bloch</p>
147
<li><p><em>Effective Java</em>- Joshua Bloch</p>
148
</li>
148
</li>
149
<li><p><em>C++ Templates: The Complete Guide</em>- David Vandevoorde</p>
149
<li><p><em>C++ Templates: The Complete Guide</em>- David Vandevoorde</p>
150
</li>
150
</li>
151
<li><p><a>cppreference.com</a>- документация по C++</p>
151
<li><p><a>cppreference.com</a>- документация по C++</p>
152
</li>
152
</li>
153
<li><p><a>docs.oracle.com/javase/tutorial/java/IandI/polymorphism.html</a></p>
153
<li><p><a>docs.oracle.com/javase/tutorial/java/IandI/polymorphism.html</a></p>
154
</li>
154
</li>
155
<li><p><a>python.org/docs</a></p>
155
<li><p><a>python.org/docs</a></p>
156
</li>
156
</li>
157
<li><p><a>Microsoft Learn - Polymorphism in C#</a></p>
157
<li><p><a>Microsoft Learn - Polymorphism in C#</a></p>
158
</li>
158
</li>
159
</ul>
159
</ul>