Как стать автором
Обновить

Что не так с ООП в 2025

Время на прочтение4 мин
Количество просмотров2.2K

Несмотря на то, что сам я ушел из большого ООП¹ более десяти лет назад, причем, надеюсь, навсегда, я всегда крайне вяло и неохотно участвую в баталиях тупоконечников и остроконечников: я абсолютно убежден, что для разных типов задач лучше подходят разные инструменты, и выхолощенное ФП заставит всех вокруг создавать тонны никому не нужного бойлерплейта для тривиального круда, а кристальное ООП — воткнет все возможные палки в колёса при реализации бизнес-процессов. Любой из современных языков программирования позволяет смешивать эти подходы, а микросервисная архитектура — даже гостеприимно приютит несколько языков и сред под одной крышей.

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

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

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

В аэропорту города N из-за погодных условий несколько часов не принимали рейсы. Некоторые самолеты ушли на соседние аэродромы, но некоторые были задержаны в портах вылета. Городок N — маленький, аэропорт тоже, обслуживается одним диспетчером. Ровно в час X у него закончилась смена, и он ушел домой. Сменщик должен был заступить на вахту спустя десять минут, чтобы посадить подлетающий еще через 20 минут самолет, из числа задержанных.

Это чуть не привело к катастрофе, потому что диспетчер следующей смены возвращался из отпуска. На задержанном самолёте. Посадить который было, понятно, некому.

Эта ситуация: самолёт ждёт указаний диспетчера, но диспетчер не сможет дать никаких указаний, пока самолёт не сядет, — и есть типичный дедлок.

Итак, мьютексы — это несомненное зло (любой, кто раскрыл рот, чтобы сообщить мне, что я просто не умею их готовить — приглашается к соревнованию на написание правильно синхронизированного кода, в который вовлечены 16 потоков). Лучшее решение, которого сможет добиться программист высокого уровня, — это вернуться на мьютексах в однопоточную модель вычислений. По сути, джавовский synchronize делает именно это.

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

public int increment() {
  int current = getDbValue();
  // управление переключилось
  setDbValue(++current);
  return current;
}

Два вызова метода increment из двух разных потоков приведет к тому, что мы дважды вычитаем предыдущее значение из базы, а потом дважды запишем обратно его же, увеличенное на единицу. Классический off-by-one в условиях высококонкурентной среды. (Для высокоинтеллектуальных комментаторов, умеющих в атомарные инкременты в своих любимых базах данных: это пример, база тут вообще ни при чем, замените базу на отсылку имейла или типа того, если вам так понятнее.)

Иными словами, столь тщательно выстроенные годами паттерны, абстракции и вообще краеугольные камни ООП — рушатся при распределении нагрузки по всем процессорам. Инкапсуляции из коробки больше нет.

С наследованием всё не так ужасно, но тоже не идеально: если в кластере одна машина перезапустилась в процессе раскатки обновлений, а вторая — пока нет, виртуальные методы будут разрешаться в инстансы разных классов (редкие грабли, да, но во-первых, я на них наступал, а во-вторых — это задача языка защитить разработчика от такого фокуса).

Среди шаблонов параллельного программирования, согласно Вике, более половины — разнообразные блокировки и остановки, но самое прекрасное — наличие там паттерна «Однопоточное выполнение». Остальные шаблоны, заботливо выдуманные за годы эмпирического анализа проблем, тоже ломаются буквально через один: Singleton, Factory (если нужен учёт создаваемых объектов), Composite, Strategy, Observer

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

Чтобы победить родовые травмы объектных языков программирования, инкапсуляцию придётся принести в жертву иммутабельности, а наследование — полиморфизму и инъекции зависимостей. Иными словами, придётся писать практически в функциональной парадигме, попутно сражаясь с примитивами языков, принципиально заточенных в бо́льшей степени на объектную модель.

Такие дела, и удачной объективизации!


¹ Для занудных буквогрызов: я использую термин ООП не в первородном смысле, в котором его первым употребил Алан Кай, а в том, которое повсеместно распространилось сейчас с легкой руки Гослинга — наследование, инкапсуляция, классы. Простите.

² Отсылка к десятому правилу Гринспена.

Теги:
Хабы:
+6
Комментарии139

Публикации

Ближайшие события