Статья

Почему сообщество программистов сошлись на парадигме ООП?

Собственно вопрос в заголовке. Почему сообщество программистов сошлись на парадигме ООП и везде, даже в небольших проектах пытаются её использовать, подтягивая десятки и сотни зависимостей? К примеру в PHP есть фреймворк Laravel, использование которого в небольших проектах, на несколько действий (генерация чего-то, комментирование, постинг, калькуляция и т.п.) само по себе иррационально, т.к. задействуют менее 20% функционала. Все равно, что забить гвоздь не молотком, а экскаватором. Не понимаю почему написание в процедурном стиле и модульность зависимостей стали моветоном.

19 Ответов

  1. По моему мнению от ООП наоборот уже отходят

    1. Последние прочитанные статьи подтверждают обратное. Даже этот проект придерживается ООП, хотя его можно уместить в сотню функций. Те же codecanyon сплошь и рядом завалены Laravel проектами, хотя по сути возможности фреймворка избыточны для них.

  1. Тут есть пару статей про ООП.

    Чем быстрее вы откажетесь от ООП, тем лучше для вас и вашего программного обеспечения и далее тот же автор пишет:

    а я вообще не вижу ООП, ни у кого нормального.

    И сделал обращение: пожалуйста, пришлите мне примеры хорошего кода ООП

    Теперь касаемо: почему сообщество программистов…

    Вот это без понятия.

    Я на LibArea начал изучать PHP т.к. мне надо было сделать проект этот именно на PHP. 😄

    1. Я на LibArea начал изучать PHP т.к. мне надо было сделать проект этот именно на PHP.

      Так она так же написана в парадигме ООП). Ну, по большей части. Я пытался написать небольшой проект, который в процедурном стиле занял бы 5мб кода, в ООП занимает более 15 + проект не завершен. Отладка, тесты, инкапсуляция, абстракции и прочие паттерны приводят к тому, что казалось бы простой код разрастается на 20 файлов и где именно происходит падение становится сложно отследить. В то время когда при процедурном подходе всё четко и ясно. Есть строка, есть ошибка. Код выпролняется сверху вниз. Тестами покрыл и наслаждаешься. Посмотрел все более менее популярные движки, по типу DLE, InstantCMS, Magenta, Wordpress там в целом смешанный подход. Используют процедурку с элементами ООП. Вот мне и стало интересно, сообщество разработчиков усложняет написание кода, т.к. кодовая база увеличивается в несколько раз, а следовательно и оплата растет пропорционально, или действительно такая проблема? Я не беру сейчас в пример энтерпрайз проекты с сотнями разработчиков и десятками микросервисов. Там каждый сервис на своем языке могут писать. Я в целом про 80% веба и систем на PHP

      + Подытожу. Я считаю преднамеренными действиями сообщества программистов для увеличения кодовой базы и, соответсвенно стоимости оплаты. Всё это подано под соусом удобства разработки командами, повторным переиспользованием и вот этим вот всем, но что мешает набор функций распределять по разным файлам с четким названием? Да и мультиинструмент всегда хуже узкоспециализированного. переиспользовать код, а после внесения правок в класс, при необходимости, обрушить систему, т.к. забыл где и когда переиспользовал метод класса или сам класс, или команда других разработчиков наследовала твой класс и написала своего динозавра — такое себе. По мне есть четкая функция и она выполняет только определенные действия. Меняя её четко понимаешь, что везде логика срабатывания едина и изменения её не принесут неожиданного результата

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

        1. Так это плюс же? Загружать только то, что надо. Что бы 2 раза не объявить достаточно в файле функций написать пространство имён. namespace Animals\Dogs;

          В файле, где используете : use Animals\Dogs; и функции вызываете Dogs\woof($yes){}

          Всё очень прозрачно и чисто по сравнению с авто загрузчиком ненужных классов

          1. Предлагается выше, что один файл — одна функция. В классе может быть несколько методов публичных, файл один, здесь же один метод видимо равен одной функции и несколько файлов в итоге загружать. Как вы представляете автозагрузчик таких функций в нужных местах?

            1. Предлагается выше, что один файл — одна функция

              Где предполагается? 1 файл — набор функций определенной группы с указанным namespace.

              Ввиду отсутствия мутабельности и наследования нет мондража при оптимизации или обновлении файла под условно новую версию PHP. В случае с ООП придется проверить всех наследников, либо экстраполировать, создавая новые наследования и в них вносить изменения, что может привести к пропорциональному росту проекта, где первыце классы будут устаревать без изменений, но не смогут выбыть ввиду зависимостей наследователей. Так же, кто мешает создать автозагрузчик файлов определенных групп через spl_autoload_register и далее использовать config\database();?

              1. что мешает набор функций распределять по разным файлам с четким названием?

                Разве здесь не это подразумевалось? Просто если мы в один файл помещаем несколько функций, а нужна только одна, зачем грузить другие?

                В целом ООП — это больше теория, на практике рекомендуется больше композиция, чем наследование.

                1. Если обо мне, то нет.

                  Я в контексте, к примеру роутинга. Набор функций, которые последовательно настраивают маршрут. Или набор функций для уведомлений. Показать непрочитанные уведомления, показать непрочитанные сообщения. Количество сообщений, общее количество. Ну и т.д. т.е. все в контексте уведомлений. Всё лежит в директории user например с namespace Users; всё чётко структурировано.

  1. ООП — сейчас обязателен для командной разработки. Ео если вы в одиночку делаете проект и никому не показываете, то там может быть все процедурное, но со временем, когда он разрастется, трудно будет это все поддерживать, хотя таким образом можно огромный проект написать и он будет работать. А вот когда кто-то захочет присоединится или вы сами через пять лет решите восстановить в памяти, где там что, с этим будут проблемы. С классами хотя бы какая-то структура вырисовывается.

    1. В чем трудность, просто на примере можете показать? Какая структура с классами? Самая дичь в том, что они мутабельны и не очевидны. Если добавить SOLID, то проект разрастается до таких размеров, что требуется несколько команд вместо нескольких программистов. Я написал выше про пространство имён которое легко решает структурирование и упрощает работу команд. Добавив use_strict=1, всё становится прям совсем очевидно. С чем проблема в функциональном подходе?

      + На примере выше, никогда не делаю мутабельны вещи. Если есть животное, то определяю его в отдельные функции. Создаю директорию animals и внутри dogs, cats, birds с неймспейсом. Всё. Если заинклюдил файл и вдруг разные команды без тестов(а они при функциональном подходе прям супер очевидны) написали одинаковые функции, то вызвать meow() не выйдет без очевидного Cat\meow() так же можно указать какие именно функции вызывать use function Animals\Cat\meow;

      + Да, вызов при use function Animals\Cat\meow;

      будет просто meow();

      + Просто проект динозавр с проде может стать реальным динозавром. Все эти наследования могут привести к тому, что программисты не трогают класс т.к. хрен знает где созданы экземпляры этих классов ещё и с extends породил десятки основанных на этом классе. Попробуй изменить. Вся цепочка упадет. Если подключаешь каждый файл сгруппированных функций, то вместо создания дочернего класса докидываешь просто новую функцию в файл.

      + Подытожу.

      Если программисты нормальные, то они и именуют функции в соответствии с действиями. Добавив пространство имён, модно группировать по любым каталогам — Animals\Dogs; Modules\Weather; Plugins; Filters; и так далее пока словарный запас не закончится.

      1. Теоретически проблем может и не быть, я даже написал, что можно процедурно написать проект на PHP и он будет работать. Можно даже без функций такое сделать. WordPress кажется, так и начинали:) Просто я не утверждаю, какой-то один подход корневым образом отличается от другого, там дело привычки. А насчет структуры… Методы в классах можно делать публичными и защищенными, как этого добиться с функциями?

        1. Это добивается путем согласованности. Например все публичные функции пишем в snake_case или camelCase, а вспомогательные с __ или другим префиксом. Согласовать со всеми командами более чем легко, написав небольшой readme.md, тем более во многих языках написание определяет многое, как CONSTANT, $_SERVER и прочие синтаксические конструкции. Ну и следовательно такую функцию можно вызвать только преднамеренно. И логично, если разработчик подключает файл группы функций, то он осознано будет использовать его, что прямо не гарантировано при автозагрузчике классов

          + Если есть наглядный пример из прода, напишите его, где классы решают проблему, которую не решают функции.

          Я просто наоборот, не вижу преимущества классов. Всё сообщество разработчиков вроде за строгую типизацию, но в тоже время используют мутабельность. Как такое возможно? Типа метод должен возвращать всегда int, но создав экземпляр класса можно его переиспользовать не для dogs, а для cats? Конечно, пример так чебе, но смысл его ясен вероятно

          + Если функции в одном пространстве имен, то с аналогичным названием функции, будут в другом пространстве имен. Если же функции по сути своей совпадают, то их можно использовать в новом файле пространства имен без переопределения класса или абстракций. Мне кажется это более прозрачно. Так же тесты легче проводить в виду изолированности групп функций. т.е. 1 файл — 1 тест. без зависимостей, без вложенности и мутабельности. Всё явно и просто для отладки. А как с тестами в дочерних классах? Что бы оттестить класс, надо размотать всю цепочку? И даже размотав нельзя поправить, т.к. базовый класс может похерить все наследования, абстракции и экземпляры, которые отличаются от базового(что логично).

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

            Без фреймворка многие вещи нужно будет реализовывать самому. Для интерпрайз такой подход невыгоден (и еще по многим причинам), для собственного небольшого проекта наверное нормально, но опять же нужен фреймворк заранее подготовленный для такого. Может быть вы знаете такой?

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

            По автозагрузчику функций тоже нет ясности, где писать require, который загружает необходимую функцию для другой функции? В этом же файле? Тогда это будет require_once во избежание конфликтов, который не особо хорош для производительности. Да и вообще разделение на большее количество файлов проблемно для производительности в целом.

  1. Для интерпрайз такой подход невыгоден (и еще по многим причинам)

    Я думал в ентерпрайзе всегда самописы. Laravel — для 80% проектов избыточен. Покажите мне хотя бы один проект, где Лара уместна. В крайнем случае CI4.

    И еще такой вопрос, насчет состояния. Вот есть данные запроса и данные пользователя. В каком виде это будет кочевать по функциям? В виде массивов?

    Данные все в БД, будь-то Редис, Мускул, или Постгрес.

    Но тогда как мы отличим массив с данными пользователя от какого-либо другого? Возрастает вероятность ошибки

    Честно, немного не понимаю по поводу данных. Делаем запрос через функцию к БД. Получаем массив данных. Присваиваем его переменной. Всё. В переменной данные пользователя либо пустой массив. Далее через функцию структурируем или сортируем, разделяем. Зачем данным пользователя или приложения кочевать по приложению? Получили, обработали, вернули. Основной принцип ФП это получить данные, обработать, вернуть данные, без побочек. Строгую типизацию задаем в файле declare(strict_types=1); функции явные без побочек, например function getSessionValue(array $session, string $key, $default = null) { return array_key_exists($key, $session) ? $session[$key] : $default; }

    По автозагрузчику функций тоже нет ясности, где писать require, который загружает необходимую функцию для другой функции? В этом же файле? Тогда это будет require_once во избежание конфликтов, который не особо хорош для производительности. Да и вообще разделение на большее количество файлов проблемно для производительности в целом.

    require_once и require особо не заметите разницу при подключении, даже десятка файлов. Но, если подключите 2 одинаковых файла через require, то получите ошибку redeclare по функциям, что прямо укажет на ужеподключенный файл. Так же через spl_autoload_register можете подключать группу файлов с namespace к примеру. определили логически функции в директории src/users/notify, message, profile к примеру и через автозагрузчик коннектите группу файлов с namespace Users; Всё. Используете методы. Но опять же, прелесть в тонкости. При автолоадере классов вы тяните все, даже те, которые не используете. Например класс сессий или коннекта к БД, хотя страницы статичны. Зачем? Это как на странице без интерактива использовать vuejs.

    Про большое количество файлов — это как раз к ООП вопросы и в частности к SOLID. Там класс одно действие. Если есть к примеру фильтры для одной группы товаров, то каждый фильтр для числовых значений, для строк, для массивов рекомендуется в разные классы через наследование и абстракции используем. Там то, что в несколько точечных функций можем уложить, раскидываем по сетке классов с приватными аргументами, методами и прочим. А после юнит тесты попробуй написать еще и где упадет найди после

    1. Ларавел просто здесь уже упоминался. В целом в интерпрайз много чего встретишь, но ООП там обязательно и это только небольшая часть. Всякие гексагональные архитектуры, диптреки, линтеры, анализаторы кода и прочее. Это все подразумевает наличие классов, как и сторонние библиотеки (с ними то что делать?). Сюда включены все крупные компании занимающиеся коммерческой деятельностью. Проекты там десятки тысяч строк (если монолит), не считая папки vendor и на php это хорошо ложится, так как он не компилируемый. То есть тут не вариант склонять их на смену парадигмы, так как иногда сложно такой проект просто на следующую версию php перевести…

      Касаемо начать новый стартап в новом видении разработки, ну попробуйте, потом расскажете;)

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

      Если брать небольшие проекты, ну тут уже указал, что будут проблемы с подбором команды.

      PS По части загрузки файлов тут не кажется более удобным, чем в PHP. Там автолоадинг прямо по неймспейсам. Все само работает.

      По совместимости — как быть со сторонними библиотеками? Там все классы.

      Решением тут может быть переписать такие либы через нейросеть, но такие объемы все равно не потянуть, а еще тесты и поддержка версий.

      PPS Под «состоянием» имел ввиду текущие данные, которые обрабатывает код, в крупных проектах есть контракты, интерфейсы и прочее, чтобы не путать данные. Представляете, если ваш банк, который переводы делает онлайн будет хранить данные простыми массивами?

      PPPS И вообще если принципиально не использовать ООП, как быть со стандартными классами PHP, например ошибками? Там есть определенная иерархия, начиная от общего интерфейса.

      PPPS По поводу «класс одно действие» это неправильно трактуется. В оригинальной книге Р.Мартина «классом должен заведовать один актор», в этом плане класс вмещает больше, чем одна функция, которая в теории должна выполнять идемпотентные операции над чем-то одним.

      1. Но как же SOLID?

        Single Responsibility Principle один класс одна ответственность. Или принцип разделения интерфейсов. класс вмещает почти всегда одну публичную функцию и множество приватных и протектных.

        Закрытость классов. Не меняем классы, а создаём интерфейсы. Т.е. базовый класс превращается в древнее мамонтовское.

        Если вопрос ко мне, то я в целом не против классов. Я их использую, но не расширяю, не наследую. Я против ООП в принципе мутабельности, наследования, один класс одно действие. Если использую классы, то без переопределения. Если можно обойтись функциями, то использую их во избежании тонны ненужного кода и простоты отладки с чтением. Опять таки, не все проекты enterprise банковские со сложной логикой. Конечно, когда речь заходит о таких проектах, то в принципе парадигмы не работают, и.к. каждый микросервис может быть написан на разных языках с разными подходами, после скомпилирован и забыт, пока не начнет протекать.

  1. Может в статью перевести? Попробовал и тут иерархия комментах сразу появилась. А то формат Q&A не подразумевает дискуссию.