Простыми словами о полиморфизме: 5‑я часть гайда по ООП
Объясняем сложную концепцию объектно-ориентированного программирования на примере интернет-магазина.
Иллюстрация: Катя Павловская для Skillbox Media
Если вы начали изучать объектно-ориентированное программирование, то наверняка слышали о четырёх его главных концепциях: абстракции, инкапсуляции, наследовании и полиморфизме. Первые три мы подробно обсуждали в других статьях, а в этой поговорим о полиморфизме — разберёмся, что это такое, как работает и в чём его практический смысл для разработчика.
Перед началом: многие нюансы из этой статьи будет сложно освоить без понимания сути и основных принципов ООП. Если хотите освежить свои знания, почитайте наш подробный материал.
Все статьи про ООП
- Что такое классы и объекты.
- Особенности работы с объектами.
- Модификаторы доступа, инкапсуляция.
- Полиморфизм и перегрузка методов.
- Полиморфизм.
- Наследование и ещё немного полиморфизма.
- Абстрактные классы и интерфейсы.
- Практикум.
Что такое полиморфизм
Мы уже знаем, что в ООП все программы состоят из объектов. Но у разных на вид объектов может быть одинаковый интерфейс — какие-то общие методы, которые они выполняют каждый по-своему. Например, у объектов «квадрокоптер» и «самолёт» общим методом будет «летать».
Так вот, полиморфизм даёт возможность использовать одни и те же методы для объектов разных классов. Неважно, как эти объекты устроены, — в ООП можно сказать самолёту и квадрокоптеру: «Лети», и они будут делать это как умеют: квадрокоптер закрутит лопастями, а самолёт начнёт разгон по взлётно-посадочной полосе.
Грубо говоря, полиморфизм — это диспетчер в аэропорту. Ему неважно, какую топливную систему предусмотрел авиаконструктор и как работает система форсажа — он просто даёт команду: «Взлёт разрешаю». После этого на лайнере начинают происходить какие-то свои внутренние процессы, на которые диспетчер уже не влияет.
Минутка семантики. Слово «полиморфизм» переводится с греческого как «многоформенность». Смысл в том, что один и тот же метод может воплощаться по-разному — например, как полёт у дрона и самолёта.
Как полиморфизм выглядит в коде
Допустим, мы делаем онлайн-магазин с мерчем известного ютуб-канала. У нас есть три вида товаров: футболки, кружки и блокноты. Задача — сделать так, чтобы все их можно было складывать в корзину и сайт каждый раз автоматически выдавал покупателю сообщение: «Товар такой-то добавлен в корзину». Как это сделать?
Есть два варианта. Можно писать свою версию метода «добавить в корзину» на каждую категорию товара — но это долго, да и код получится неопрятный. А можно написать один полиморфный метод, а потом использовать его для каждого нового объекта — и вот это как раз наш случай. Разберём весь процесс пошагово.
Шаг 1. Создаём базовый класс «Товар»
Базовый класс описывает общие свойства объектов. Например, у нас это название товара и возможность добавлять его в корзину.
Пока всё это выглядит очень абстрактно, но дальше — больше.
Шаг 2. Создаём три производных класса: «Футболка», «Кружка» и «Блокнот»
Производные классы расширяют функциональность основного. То есть задают какие-то дополнительные параметры — в нашем случае это характеристики товаров: размер футболки, объём кружки и количество страниц у блокнота.
Шаг 3. Вызываем метод «Добавить в корзину»
Допустим, пользователь зашёл в наш магазин и решил купить три товара: футболку «Кислота» 48 размера, кружку «Омут» на 250 мл и блокнот «Древо жизни» на 180 страниц. Поставил он галочки напротив каждого товара, нажал «Добавить в корзину» — и вот какая история начинает происходить в коде:
Мы объединили товары в массив и с помощью цикла применили метод AddToCart сразу ко всем. Если бы наш магазин был реальным, пользователь получил бы примерно такие сообщения:
То есть мы достигли как раз того результата, который и планировали. Запустить код программы целиком и посмотреть, как он работает, можно по этой ссылке.
Для чего нужен полиморфизм
Смысл полиморфизма в том, что нам не надо писать для каждого товара свой метод — например, какой-нибудь AddToCartShirt для футболки или AddToCartCup для кружки. У нас просто есть один AddToCart, и мы на него полагаемся. Если в магазине появятся, например, кепки, мы просто немного допилим наш метод под особенности кепок, и дело в шляпе.
Особенно это актуально в больших коммерческих программах со сложной логикой. Представьте, если бы у нас был не магазин с аксессуарами, а крупный маркетплейс вроде «Озона». Там без полиморфизма просто не обойтись — иначе код превратится в лапшу из функций, которые делают одно и то же, а называются по-разному.
Отсюда можно выделить три главных преимущества полиморфизма:
- Читаемость. Чем меньше кода и чем лучше он упакован, тем проще работать программистам и тем быстрее идёт разработка.
- Масштабируемость. Можно добавить в магазин носки, ручки, напульсники, подвески и ещё кучу разных товаров, а за их покупку всё равно будет отвечать одна команда — AddToCart.
- Предсказуемость кода. Когда у нас есть один метод для разных объектов, мы чувствуем себя спокойно. Можно не переживать, что команда «Добавить в корзину трусы» случайно применится к носкам и вызовет какой-то сбой в программе.
Что дальше
В ООП под полиморфизмом понимают только одну его разновидность — полиморфизм подтипов. Он как раз отвечает за то, чтобы объекты разных классов можно было вызывать одним методом. Но существует ещё два вида полиморфизма: параметрический и ad-hoc. О них мы и поговорим в следующих статьях — разберёмся, чем они различаются и для чего нужны.