06.08.2022

Принципы ООП. 3. Полиморфизм

Сергей Немчинский
5 минут просмотра
Принципы ООП. 3. Полиморфизм

Это третья лекция из мини-курса о принципах ООП, и тема ее – полиморфизм.

Полиморфизм, наверное, самый важный принцип ООП: на его основе строится огромное количество паттернов и решений. Именно из-за полиморфизма ООП такая мощная и классная штука, которая позволяет не писать один и тот же код по 50 раз, а дублировать его и использовать для работы с другими данными.

Определение полиморфизма – «способность функции использовать данные разных типов». Просто, да не просто. Если задуматься, то функция может использовать данные разных типов по-разному. Есть два типа полиморфизма: полиморфизм ad-hoc, то есть по запросу, и параметрический, или истинный полиморфизм.

Первый тип полиморфизма (ad-hoc) — это полиморфизм по запросу. Это банальные вещи: приведение данных – когда метод получает данные, приведенные к тому типу, с которым он обычно работает – и перегрузка методов, когда метод существует в нескольких вариантах, с одинаковым названием, но разными параметрами. Это ненастоящий полиморфизм. Если это перегрузка, то это разные методы: они имеют разное тело, какой же это полиморфизм? Если мы говорим о приведении данных, то тело метода одно, но оно работает с одним типом данных, потому что до входа в метод параметр был преобразован в нужный тип данных. Такой недоделанный полиморфизм.

Параметрический, или настоящий полиморфизм, это когда функция, одна и та же, с одним и тем же телом, может принимать в качестве параметра данные разных классов. Как это возможно? Ну, например, когда параметром функции является базовый класс для некоторой иерархии объектов. Поэтому функция может принимать любой из подклассов этого класса. Как мы говорили в прошлой лекции, наследник связан с базовым классом. Если параметр функции – базовый класс, любой наследник может прийти туда и быть обработан. Обработан он может быть по-разному, может быть одинаково, все зависит от внутренней структуры этих объектов и того, как написан метод.

О полезности полиморфизма. Как-то в книге кого-то из великих программистов я встретил мысль, которая изначально меня удивила. «Все IF в программе можно заменить полиморфизмом». Мысль о том, что все условные ветвления в программе можно заменить полиморфизмом, поначалу взорвала мне мозг. Но затем я понял, что действительно, любой if в программе можно заменить на полиморфизм, т.е. Одна ветка уходит в одного наследника базового класса, другая ветка (else) — в другого. Если нет никакой ветки, то остается пустое место — метод ничего не делает.

Если вы задумаетесь, то поймете, что полиморфизм помогает уменьшать размер программы на порядки. Именно с помощью полиморфизма вы сможете обеспечить гибкость и избежать перегруженности кода. Самый страшный код, который попадался мне в жизни – это пять тысяч строк if-ов. И вся эта простыня в пять тысяч строк кода могла быть схлопнута в небольшое дерево наследования. Это один из самых распространенных способов рефакторинга. Полиморфизм как раз является способом избежать запутанного, сложного и трудно поддерживаемого кода.

Современная программная инженерия пришла в состояние, когда мы практически отказались от наследования. Нет наследования – нет истинного полиморфизма. Но если взглянуть на все существующие паттерны, начиная с Декоратора, почти все они построены на полиморфизме. Если в вашем коде нет наследования, нет полиморфизма, то ваш код не является ООП-кодом. Это очень плохо.

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