ООП было доминирующей парадигмой в программировании последние 30 лет. Для большого количества программистов ООП - это и есть Программирование, будто бы когда-то древние люди программировали в императивном стиле, а потом случился прогресс и пришло спасение, и все поняли “как надо”.
Вместе с тем есть ощущение, что последние годы нарастает ощущение некоторого разочарования в парадигме.
Впервые оно пришло, кажется, со стороны игровой индустрии
(первая его весточка - интерес к Data Driven Design
как альтернативе ООП, известное видео по теме).
Но также растёт интерес и к функциональному программированию с его обещанием statless
работы с данными, надёжность и простота в тестировании.
Мы вспомнили, что даже многие святые Computer Science не всегда хорошо отзывались об ООП (Линус, Дийкстра).
Я ни в коем случае не думаю, что ООП это всегда плохо, но как и с большинством решений оно решает одни проблемы ценой создания других.
Как минимум, некоторые плюсы, которые приходят на ум:
- Думаю, что для человека естественно моделировать окружающий мир в концепции “объектов” - т.е. вещей которые ведут себя так-то, и взаимодействуют друг с другом. Это гораздо понятнее, чем представлять себе “конвеер” функций, обрабатывающих данные.
- Моделирование предметной области: к сложной проблеме можно подступиться, разбивая её на отддельные, но значимые части
- Большая гибкость ООП-интерфейсов: в удачных случаях изменения “приращиваются” быстро простым расширением класса.
Даже известное уже видео с критикой ООП (https://www.youtube.com/watch?v=QM1iUe6IofM) не утверждает, что оно всегда плохо - в нем большее внимание уделяется проблеме использования его не для высокоуровневых абстракций, а вообще любых вещей.
Причин для критики ООП немало, попробую выделить 2 основные:
- Производительность - за этой критикой отсылаю на материалы про
Data Driven Design
, но в кратце: массивы указателей на объекты хуже для кэширования чем массивы данных, лишние уровни опосредованности также не добавляют производительности. Все эти проблемы критичны для индустрии вроде игр, но очевидно также затрагивают и другие области. Overengineering
- тот самый FooBarBazzEnterprize Edition, только уже не в формате шутки, а прямо в вашем производственном коде. Overengineering - это, похоже, не какое-то отклонение, а естественная тенденция, к которой ведёт парадигма ООП. “Алгоритм” решения задач это не первый инструмент ООП - в первую очередь мы моделируем область, а когда получается плохо, решение также продолжается на уровне моделирования - новые объекты и классы добавляются чтобы “доработать” нашу текущую схему и для “обслуживания” уже существующих объектов (всегда вспоминаю рант Шона МакГрафа по поводу кода, который написал человек, издавший не одну книгу по C++). Моделирование проблемы в таком стиле ставится на первое место, хотя решением могло бы успешно быть просто последовательностью преобразования данных. Вместо этого всё должно быть представлено как взаимодействие “скрывающих своё состояние” объектов - подход, который может иногда преобразовать прямолинейный алгоритм в “а где собственно код?” архитектуру.
Как и всякий набор правил, существующий достаточно долго, ООП где-то превращается в религиозную догму со своими “священными текстами” и набором заповедей.
Если ты считаешь, что вот здесь ООП плохо подходит для решения задачи, то не факт, что тебе объяснят, почему. Скорее отошлют читать такой то священный текст (Clean Code
например).
Кроме того, из-за того, что концепция настолько размыта, что также существует и набор конфессий, правила которых будут взаимоисключающими.
Правильного ООП™
увы, вероятно не существует. Фанаты Smalltalk
не согласятся с тем, что C++
хорошая репрезентация его идей
В качестве реакции на тот же SOLID возникли и другие абревиатуры (см. GRASP и CUPID).
Нет, я не перешёл полностью на сторону ФП или императивного кода. Хотя бы потому что от ООП никуда не деться. К тому же охотно верю, что можно написать хороший (не идеальный или идиоматический) ООП код, если подходить к нему в первую очередь как к инструменту для решения текущей проблемы.
Вещи вроде SOLID - не более чем рекомендация, не нужно фанатизма в его соблюдении.
Может быть не стоит создавать класс с конструктором из 15 параметров только потому что нужно соблюсти DI, которые нам все равно
придётся потом включить уже с помощью DI-контейнера
или огромной Main
функции, или выделять 50 маленьких интерфейсов для ISP, потому что так нужно и “гибко”.
Это как ставить телегу впереди лошади. Принципы и паттерны должны органично всплывать в мыслях там где они естественны, а не “а какой паттерн мне применить здесь”. Как будто проблемы ставит нам не реальный мир, а само ООП.
Кто-то наверное скажет: “ну да, это же очевидно”. Для меня нет, я многократно испытывал неуверенность и впадал в ступор, если мне начилало казаться,
что я пишу “неидиоматическое ООП”, что я обязан абстрагировать вообще любой класс - а вдруг мне понадобиться “подменить” реализацию моего S3-клиента
(ведь завтра может прийти наверное S4-клиент
), что мне обязательно нужна “фабрика” (лучше абстрактная) и вообще никак нельзя чтобы один класс создавал
объекты другого если он не фабрика. Что если класс больше чем на 200 строк кода - то это плохой класс (предлагаю заглянуть в .NET классы, ага).
У меня нет лучшего совета, чем “просто написать нормально” - исходя из собственного опыта, соседнего кода и интуиции. Оставить ООП перфекционизм любителям споров в Интернете.