Рубріки: Мнение

Стало массовым, но уже устарело: пять самых больших проблем объектно-ориентированного программирования

Оленка Пилипчак

Компьютеры, которые использовались для программирования в 1960-х годах, были не настолько мощными, как сейчас. Те незначительные ресурсы, которыми они обладали, нужно было распределять между структурами данных и процедурами.  

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

Затем в 1966 или 1967 годах появился Алан Кэй. Он предложил использовать инкапсулированные мини-компьютеры, обменивающиеся не данными, а сообщениями. Так можно было сэкономить вычислительные ресурсы.

Несмотря на гениальность идеи, объектно-ориентированное программирование стало популярным не сразу. Но после 1981 года ситуация изменилась и уже несколько десятилетий она привлекает как новичков, так и опытных программистов. 

В последнее время эту парадигму все чаще критикуют. Но действительно ли объектно-ориентированное программирование устарело?

Этот вопрос задает доктор наук по физике элементарных частиц Эри Джури. Также она разрабатывает алгоритмы искусственного интеллекта, чтобы лучше исследовать эти частицы.

Передаем ей слово.

Неужели сочетание функций с данными — вздор?

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

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

Сначала об этом речи не шло, но сейчас для объектно-ориентированного программирования важны также наследование и полиморфизм.

Наследование означает, что разработчики могут задавать подклассы, обладающие всеми свойствами родительского класса. Это понятие появилось в объектно-ориентированном программировании только в 1976 году, спустя десятилетие после его возникновения.

Спустя еще 10 лет начали использовать полиформизм: это означает, что метод или объект может быть шаблоном для других. В определенном смысле это обобщение наследования, поскольку не все характеристики исходного способа либо объекта нужно передавать новой сущности; вместо этого можно изменить свойства.

Особенность полиморфизма: даже если две сущности зависят друг от друга в исходном коде, эта сущность работает больше как плагин. Это упрощает жизнь разработчиков, поскольку им не нужно беспокоиться о зависимости во время выполнения. 

Отмечу, что наследование и полиморфизм есть не только в объектно-ориентированном программировании. То, что его на самом деле отличает — это инкапсуляция относящихся к ним фрагментов данных и методов. В 1960-е это была просто гениальная идея.

Пять больших проблем объектно-ориентированного программирования

Когда объектно-ориентированное программирование стало массовым, оно трансформировало способ видения кода разработчиками. До 1980-х годов в программировании преобладало процедурное программирование, которое было очень машинно-ориентированным. Разработчики должны были многое знать о том, как работают компьютеры, чтобы написать хороший код.

Благодаря инкапсуляции данных и методов, объектно-ориентированное программирование сместило акценты в разработке программного обеспечения на человека. Именно человеческая интуиция подскажет нам, что метод drive() относится к группе данных car, а не к группе teddybear.

Появившееся позднее наследование также было интуитивно понятным. Вполне логично, что Hyundai — подгруппа автомобилей и обладает теми же свойствами, аPooTheBear — нет.

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

1 Проблема банана, гориллы и джунглей

Представьте, что вы настраиваете новое приложение и думаете, как создать новый класс. И тут вы вспоминаете о прекрасном маленьком классе, который создали для другого проекта. Этот класс идеально подошел бы и сейчас. 

Никаких проблем! Вы можете повторно использовать класс из старого проекта. Но есть один нюанс: этот класс может быть подклассом другого класса. Потому вам нужно будет добавить и родительский класс. Затем вы поймете, что родительский класс зависит от других классов. В результате вы должны добавлять кучу кода.

Вот известная цитата создателя Erlang, Джо Армстронга:

«Проблема с объектно-ориентированными языками состоит в том, что они — сложный комплекс. Вы хотели только банан, но вдобавок получили держащую его гориллу и джунгли, где она живет».

Этим все сказано. Да, возможность повторно использовать классы — едва ли не основное преимущество объектно-ориентированного программирования. Но не стоит использовать его всегда: иногда лучше написать новый класс. 

2 Проблема хрупкого базового класса

Представьте, что вы успешно использовали класс из другого проекта для нового кода. Что произойдет, если базовый класс изменится?

В результате может пострадать весь ваш код, хотя вы ничего на нем не меняли.

Однажды ваш проект может прекратить работу, потому что кто-то изменил незначительную деталь в базовом классе, и это стало роковым для вашего проекта.

Чем больше вы используете наследование, тем больше обслуживания придется предоставлять. Поэтому хотя повторное использование кода кажется очень эффективным в краткосрочной перспективе, в долгосрочной перспективе это может оказаться неудобным.

3 Бриллиантовая проблема

Наследование — прекрасная вещь: с его помощью мы можем брать свойства одного класса и передавать их другим. Но что, если нужно смешать свойства двух разных классов?

А вот это вы уже не можете сделать. По крайней мере, элегантным способом. Рассмотрим, к примеру, класс Copier. (Я позаимствовала этот пример, а также некоторую информацию о рассматриваемых проблемах из текста Чарльза Скальфани «До свидания, объектно-ориентированное программирование»). Копировальный аппарат сканирует содержимое документа и печатает его на пустом листе. Какой это должен быть подкласс, сканера или принтера?

Правильного ответа просто нет. И эта проблема возникает довольно часто, чтобы вызвать беспокойство у программистов.

4 Проблема иерархии

В бриллиантовой проблеме вопрос в том, подклассом какого класса является Copier. Но я вам солгала — хорошее решение есть. Пусть Copier будет родительским классом, а Scanner и Printerподклассами, которые наследуют только подмножество свойств. Проблема решена!

Но что произойдет, если ваш копировальный аппарат не работает с цветом, а принтер может печатать цветные изображения? Не является ли принтер в этом смысле обобщением копировального устройства? Что делать, если принтер подключен к Wi-Fi, а копировальное устройство — нет?

Чем больше свойств у вашего класса, тем труднее установить правильную иерархию. Вы имеете дело с кластерами свойств, где Copier используют некоторые, но не все свойства Printer, и наоборот. Если вам нужно определить иерархию в большом сложном проекте, это может привести к беспорядку. 

5 Проблема взаимосвязанности

Вы можете сказать: хорошо, тогда мы не будем использовать иерархию в объектно-ориентированном программировании. Вместо этого будем пользоваться кластерами свойств и при необходимости наследовать, расширять или заменять свойства. Конечно, возникла бы незначительная неразбериха, но это могло бы сработать. 

Есть только одна проблема. Суть инкапсуляции в том, чтобы обезопасить части данных друг от друга: так вычисления становятся более эффективными. А это не работает без серьезной иерархии.

Подумайте, что произойдет, если объект A переопределит иерархию, взаимодействуя с другим объектом B. Неважно, какая связь у A с B, за исключением того, что B — не прямой родительский класс. Тогда A должен содержать ссылку на B, поскольку иначе он не мог бы взаимодействовать.

Но если A содержит информацию, которой также обладают подклассы B, то эту информацию можно изменить во многих местах. Вот и все: информация о B больше небезопасна, инкапсуляция не работает.

Хотя многие программисты создают программы с такой архитектурой, это не объектно-ориентированное программирование. Это беспорядок.

Опасность единой парадигмы

Эти проблемы возникают тогда, когда наследование применяют там, где это не самое лучшее решение. Поскольку наследование не было в первоначальной идее этой парадигмы, я не говорила бы, что это проблемы лишь объектно-ориентированного программирования. 

Но переусердствовать можно не только с объектно-ориентированным программированием. В чистом функциональном программировании очень трудно обработать введенные пользователем данные или вывести сообщения на экран. Для этого гораздо лучше подходит объектно-ориентированное или процедурное программирование.

Тем не менее, есть разработчики, которые не понимают этого. Они пишут десятки строк кода, которые никто не может понять. Хотя, если бы они использовали другую парадигму, то с легкостью сократили код до нескольких читабельных строк.

Парадигмы немного похожи на религии. Да, возможно, Иисус, Магомет и Будда были правы в определенных вещах. Но если вы слепо соблюдаете все религиозные предписания, то вряд ли это принесет вам счастье. 

Так же и с парадигмами программирования. Сейчас функциональное программирование становится все популярнее, а объектно-ориентированное начинают больше критиковать.

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

Главный вопрос: мы на пороге новой революции?

Дискуссия о функциональном и объектно-ориентированном программировании сводится к следующему: правда ли мы наблюдаем упадок объектно-ориентированного программирования?

Часто функциональное программирование более эффективно. К примеру, для анализа данных, машинного обучения, параллельного программирования. 

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

Скорее всего объектно-ориентированное программирование будет использоваться еще не один год. Не спешите отказываться от него, но обращайте внимание и на другие парадигмы. 

Автор: Эри Джури

Текст адаптировала Евгения Козловская

Останні статті

Обучение Power BI – какие онлайн курсы аналитики выбрать

Сегодня мы поговорим о том, как выбрать лучшие курсы Power BI в Украине, особенно для…

13.01.2024

Work.ua назвал самые конкурентные вакансии в IТ за 2023 год

В 2023 году во всех крупнейших регионах конкуренция за вакансию выросла на 5–12%. Не исключением…

08.12.2023

Украинская IT-рекрутерка создала бесплатный трекер поиска работы

Unicorn Hunter/Talent Manager Лина Калиш создала бесплатный трекер поиска работы в Notion, систематизирующий все этапы…

07.12.2023

Mate academy отправит работников в 10-дневный оплачиваемый отпуск

Edtech-стартап Mate academy принял решение отправить своих работников в десятидневный отпуск – с 25 декабря…

07.12.2023

Переписки, фото, история браузера: киевский программист зарабатывал на шпионаже

Служба безопасности Украины задержала в Киеве 46-летнего программиста, который за деньги устанавливал шпионские программы и…

07.12.2023

Как вырасти до сеньйора? Девелопер создал популярную подборку на Github

IT-специалист Джордан Катлер создал и выложил на Github подборку разнообразных ресурсов, которые помогут достичь уровня…

07.12.2023