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

Комментарии 145

Просто не стоит путать ошибки людей с недочетами паттернов ООП и не терять контроль над кодом — можно написать лучше без кучи абстракций? Здорово!
Абстракции тоже бывают разные. На объектах свет клином не сошёлся.

Давайте теперь каждый комментарий на 20 строк оформлять отдельной статьей!
Больше статей богу твиттерастатей.

Ценность статьи — не в количестве букв, а в информации, которая в ней содержится. Можно было бы налить больше воды, как это было сделано в указанной ссылке на статью, но я в этом смысла никакого не вижу.

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

Главные ошибки автора:
1. Он не дает четкого определения ООП.
2. Приводит примеры плохого ООП.
3. Не приводит ничего взамен, чтобы подход был равносилен ООП. То, что приведено в конце — это слишком специфично, чтобы можно было использовать во всех местах, где используется ООП.

Можно делать разбор каждого утверждения. Но автор неправ фундаментальным образом: он боролся не с ООП, а с неправильным использованием ООП. На основе этого сделал вывод про ООП.
Вы тоже не даете определения слова «сложность», его понимания в контексте вашей статьи. Хотя именно в нем вся суть (не в слове, а в понятии).
И это не замечание вам, это проблема многих — у меня один коллега частенько использует словосочетание «управление сложностью» но на прямой вопрос «как измерить сложность, что сложнее А или Б» он либо затрудняет ответить, либо дает противоречивые ответы.
но на прямой вопрос «как измерить сложность, что сложнее А или Б» он либо затрудняет ответить, либо дает противоречивые ответы.

Внезапно, не существует точных инструментов и определения всех аспектов влияющих на сложность ПО, так же как и не существует точных едениц измерения сложности.

А вы можете, например, ответить на прямой вопрос — «Как измерить качество картины художника, что лучше, „Чёрный квадрат“ или „Терзания святого Антония“»?

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

Если вы определите какой результат вы хотите получить от действий с кодом/любой другой системой, введете «элементарные» по какому либо критерию операции над ней, и скажете что разницей в их сложности вы пренебрегаете — у вас появится инструмент для оценки сложности в контексте какой либо задачи — это просто количество элементарных операций для получения результата.

К сожалению с человеческими мозгами сложно определить множество элементарных операций и саму задачу. Все зависит от информации, которая содержится в этих мозгах.
Например, если вы занимаетесь дебаггингом, то в случае если вы знакомы с этим участком кода и знаете как оно должно работать, то ваша задача выглядит как: «Понять, что работает неправильно.» А еще вам будет легко «навигироваться» в этом участке.
Если вы не знаете как он работает, то к этому добавлятся: понять как работает код на этом участке и каковы ожидаемые результаты его работы. И навигация по коду, пока вы это делаете, уже не будет такой простой, вам придется тратить дополнительные силы на то, чтобы изучать новые для вас объекты и зависимости.

Уменьшение количества зависимостей тоже не всегда будет работать. Простой пример — если вы соберете все классы в один god object — вы уменьшите количесво зависимостей, так как сможете напрямую работать со всеми сущностями. Уменьшит ли это сложность?)
В целом, это даже возможно проиллюстрировать в тексте:
168150320941
168-150-320-941
Сообщение минимальной длины и колмогоровская сложость тоже работают не для всех типов задач, примерно по той же причине.

Я как-то пытался все это математически описать.

В общем, сложность в контексте взаимодействия человека и чего-либо — это очень сложно)

upd:
Хотя, у меня есть одна мысль как можно очень грубо описать нашу проблему. Представьте, что у вас очень маленькая оперативка, которая умеет хранить только примерно 4-8 указателей на области памяти, а все операции производятся на харде и только для двух областей памяти в один момент времени.
А, и да, результат операций, через некоторое время откатывается, поэтому для его сохранения надо переодически его повторять. Но есть вероятность, что он все же в какой-то момент сохранится.
Если интересно, можете почитать подробнее:
en.wikipedia.org/wiki/Long-term_depression
en.wikipedia.org/wiki/Long-term_potentiation
Вот примерно с такой «вычислительной машинкой» в виде мозга нам и приходится жить. Еще раз повторяю, это очень грубая и неточная аналогия, но помоему она дает некоторое представление.
Автор статьи против ООП умеет формулировать мысли так, что они понятны при прочтении. А люди, у которых есть более-менее опыт в реальной разработке, находят в его примерах отражение реального положения дел на рынке.
У Вас же даже на этапе формулирования мыслей случаются, кхм… exception'ы.
Что касается управления сложностью, то она производится на этапе формулирования требований и дальнейшего проектирования решения. Никакой код не породит столько оверинжиниринга, как изначально неоптимальные требования, которые «не ложатся» ни на потребности бизнеса, ни на технические возможности целевой платформы.
Поэтому в хороших компаниях до программиста доходит оптимальным образом сформулированная задача, которую можно решать как ему больше нравится, пусть даже и на ООП его мечты.
А в плохих компаниях всё превращается в свалку — и документация, и код, и бизнес-процессы в итоге тоже. Кстати, требование в стиле «должно быть готово вчера» — это как раз маркер плохой компании с некомпетентным менеджментом. Программист, идущий на поводу у таких требований, развращает менеджмент и бизнес, демпингует и в итоге сам себя оставляет без штанов и без выходных.
Кстати, как раз в статье приводится ссылка на наш случай.
Сейчас пойдет немного о содержании:
Главный аргумент всех сторонников ооп — просто у вас понимание ооп не то, ооп — это вот это и мне это нравится. Любые попытки переубедить ооп-чников кончаются тем, что тебе говорят, что ты просто не так понимаешь ооп. Причем все без исключений приводят разные понятия ооп. (Ну да, многие, конечно сходятся общих на аспектах: полиморфизм, инкапсуляция и тд). Также говорится о том, что в ооп записывают то, что не есть ооп — то, что с понятием объектно-ориентированное программирование связь имеет лишь в том, что может с ним использоваться (по-моему это как раз про контракты).
Я считаю, что даже если мы не имеем альтернатив, это вовсе не значит, что наше дальнейшее развитие должно быть прибито к ооп или что лучше уже не придумать. Много пустых споров рождаются всего на всего потому, что каждый под словом «ооп» видит свою боль. Просто иногда происходит диссонанс между дифирамбами о ооп, которые ты прочел в начале пути, и твоей постоянной болью… Просто нужно попробовать что-то новое (я не про существующие парадигмы как есть). Я не думаю, что после того, как мы забудем ооп наши проблемы решаться, нет. Я думаю это посыл к тем, кто потенциально может создать новую парадигму и язык для нее.
Это чем-то похоже на великих литературных классиков. Они поднимают важные проблемы, но навязывать свое мнение (как ты видишь решение, костыли также считаются) — моветон. Классиков в их время обычно критиковали за их взгляд на проблему, на которую у общества есть устойчивые убеждения. А потом мы говорим о том, что они были правы. Это, конечно, замечательно, но, ГДЕ МОЯ СЕРЕБРЯНАЯ ПУЛЯ? Ее не будет? Так что, оставить попытки ее найти?
Автор просто хотел громко назвать статью. А остальное — дело техники. Идея такая: обосрать святое, и, не предложив ничего внятного взамен, скрыться с места осрамления.
Сравните GDI и System.Drawing
Вот вам и ответ.

Скорее всего вы неправы — главное в ООП — это полиморфизм

Тоже так считаю. Попробую объяснить. Вот есть у нас в игре объекты разных типов, мы их всех можем унаследовать от некого SceneObject, у которого есть метод paint(), который у всех типов объектов разный. Тогда мы можем хранить их все в одном массиве, и делать простой paint(SceneObject[] objects). И не нужно проверять типы, чтобы дергать для одной структуры один метод, а для другой другой. Так километровые switch'и будут. А с полиморфизмом эти свитчи автоматизированы внутри таблицы виртуальных методов.

Точно так же при разработке GUI очень удобно все компоненты объединять в хорошо продуманную иерархию.

Полиморфизм — это про систему типов. А в ООПроектировании и следующем за ним ООПрограммировании — главное грамотно структурировать объекты, чтобы не было лапши, на которую жаловались в предыдущей статье. И если посмотреть на C++, то там эта задача начала решаться далеко не сразу — без именных пространств все эти классы это действительно просто лапша. А вот при разработке Java эту проблему учли и использовали ООП подход — пакеты. И частично подумали про следующий уровень (jar-архивы и возможность подключения классов/jar-архивов с помощью настраиваемого classpath)… но это было не очень удобно… И при разработке системы сборки maven следующие разработчики учли следующий уровень проблемы — инкапсуляция этих пакетов в крупные пакеты (библиотеки), чтобы можно было удобно рулить зависимостями и сборкой.
А поверх этого теперь ещё микросервисы/докеры/контейнеры/прочая инфраструктурная сложность добавилась, чтобы разделить сложность на очередные планы.

Без ООП тоже самое можно сделать.
SceneObject solider = {.x=100, .y=100, .paint = &paintSolider};
SceneObject copter = {.x=100, .y=100, .paint = &paintCopter};

А можно так:
SceneObject enemies[] = {{.x=100, .y=100, .paint = &paintSolider}, {.x=100, .y=100, .paint = &paintCopter}} 

Можно динамически поменять paintSolider() на paintAngrySolider(), можно несколько разных paint<...>() передать сразу в 1 объект.
Другое дело, что все эти вариации paint<...>() скорее всего будут очень похожи, и писать для каждого класса свой — лишняя работа, и вообще данные должны решать, как выглядит объект на экране, а не код.
Ну это в скриптовых языках. А в компилирующихся это сложнее.
Это обычный С11.
Насчёт GUI тоже сомнительно, что ООП полезно. Давно уже никто почти гуй руками в коде не верстает, всё в xml или других подобных файлах разметки. А потом ищут элементы медленными костылями навроде TextView number = findViewById(R.id.something), потом библиотеками обмазывают, чтобы скрыть это всё подальше от глаз. Хотя можно было бы использовать те же структуры, которые без лишних прослоек напрямую линкуются с кодом.
Да, но кто-то все те вьюшки написал, нет? И до сих пор фиксят и переделывают.
Вьюшки в андроиде внутри жесть жестяная) Шаг в сторону и всё, поведение инкапсулировано, приватные поля, классы и всё такое.
Да, я знаю. Работаю с этим уже много лет :)
Я только хотел сказать, что без полиморфизма и наследования этот GUI было бы в разы сложнее написать.

Это называется не "без ООП", а "эмулируя ООП".

Не, это называется «когда тебе нужно 10 функций, напиши 10 функций, а не абстрактный класс + 10 наследников».

А чем принципиально абстрактный класс и 10 наследников отличаются от объявления типа и 10ти функций?

Принципиально — указатель на функцию я могу на лету заменить, превратив один игровой объект в другой с минимальными усилиями. Был Solider — добежал до ВПП — стал Copter. Или не добежал, стал deadSolider или zombieSolider. А ООП класс придётся уничтожать и создавать на этом месте новый. Если они лежат, например, в ArrayList — сначала уничтожаем, сдвигается N элементов списка, потом вставляем, опять N сдвигов, тратим время своё и CPU, и создаём мусор для GC.
По удобству — писанины меньше и всё на виду. Если мне нужно скажем PowerBonus отрисовать, не нужно открывать и править все 10 классов, если они в отдельных файлах, как в джаве.

Но зачем при замене объекта удалять его из ArrayList с последующей вставкой нового? Почему нельзя просто заменить объект по известному индексу?


Если мне нужно скажем PowerBonus отрисовать, не нужно открывать и править все 10 классов, если они в отдельных файлах, как в джаве

А может, достаточно не складывать их все в отдельные файлы?

Можно, но всё равно оверхэд и лишняя писанина останутся. И нужна ссылка на список, ссылки на саму структуру недостаточно.
А если кроме Paint() есть ещё AI(), который отвечает за поведение? Есть вражеский солдат, есть свой, а ещё он может быть повержен в бегство, оглушён, отравлен или ранен(будет искать место, где отсидеться можно), получается для каждой комбинации внешнего вида и поведения свой класс? Или же приходим к тому же switch, от которого хотели уйти.

Не обязательно сваливать все ответственности в один класс.

О боже… и такие граждане спорят об ООП… Выучите пожалуйста сперва матчасть!
ArrayList хорош при добавлении в конец, чтении и произвольному доступу по индексу, как только возникает необходимость динамически удалять/добавлять объект — выкидываем нафиг ArrayList и заводим LinkedList. Да у нас будут большие проблемы при работе с индексами… но как часто вы их используете-то? В 5% случаев в лучшем случае они нужны, в остальных хватает простого итератора по списку.
Далее "уничтожаем, сдвигается" — а нафига ж удалять-то вообще из списка, если мы тут же вставляем другой объект? Взяли да вставили новый на место старого, а старый GC и без вас прекрасно подберёт. И тогда и от ArrayList-а отказываться становится без надобности.


По удобству — писанины меньше и всё на виду.

По удобству — писанины-то может и меньше, а вот контроль типов утерян напроч… и зачем тогда морока с типизированым языком? JS — ваше всё. А нам дайте работу с типизированными языками, которые путём добавление небольшого количества бойлерплейта защищает от кучи глупых ошибок.


Если мне нужно скажем PowerBonus отрисовать, не нужно открывать и править все 10 классов, если они в отдельных файлах, как в джаве.

Ну так вынесите свой PowerBonus в родительский-абстрактный для этих 10 классов класс… а ещё можно вынести его в утилитарный и ещё целая куча альтернатив. И да Java требует писать бойлерплейта в среднем больше других языков, но меня вот это мало смущает — взамен достаточно многое даётся.

Да шо вы меня учите — я вас не просил. Я для себя джаву давно выбросил на помойку, как и всё ООП, зачем я буду страдать всей это фигнёй.
Да у нас будут большие проблемы при работе с индексами… но как часто вы их используете-то?
У меня не будет — вы же это предложили, а не я. По сравнению с процедурным вариантом что то, что это посасывает. В случае с джавой сначал нужно получить из списка исходный объект, потом создать новый, потом скопировать его поля (memcpy в джаву не завезли, лол, придётся ручками каждое поле, которое должно остаться неизменным, переписывать), и только потом вставить. Нет уж, сами занимайтесь этим мазохизмом. Когда на древнем С это просто как
enemies[position]->paint = &somepaint
.

Это и на джаве выглядит как enemies[position].paint = somepaint при правильно проведенной декомпозиции.

ИМХО полиморфизм (кстати, вместе с наследованием) можно делать кучей способов, и без ООП.

В вот в ООП ИМХО главное — это идея инкапсуляции.
Если чуть подробнее, то разделение на данные — т.е. память без поведения (числа, строки, даты и т.д.) и на объекты — память, которая имеет поведение, т.е. у него есть набор функций, которыми с ним можно взаимодействовать — интерфейс, но при этом к внутренностям которого никто не должен иметь доступа (кроме самого объекта, естессно).
Да, иногда явного принуждения в языке нет и можно сделать объект без поведения (ака структуры), к данным которого можно получить доступ.
Но это не умаляет мощь концепции — если объекты А и Б удовлетворяют одному и тому же интерфейсу В, то их не просто можно обрабатывать единообразным образом как В, а это будет даже явно задано (потому что вы не имеете доступа к внутренностям реализации).
можно делать кучей способов, и без ООП
Можно пример? Сразу говорю, если там есть указатели на функции в составе структуры — то это просто велосипедный способ использовать ООП.

Что касается инкапсуляции, то она есть в чистом C, и там она сильнее чем в C++.

Например, через протоколы вы можете достигать полиморфизма даже от типов, к реализации которых не имеете доступа.
Пример: https://medium.com/everydayhero-engineering/extensibility-in-elixir-using-protocols-2e8fb0a35c48


Ещё один вариант: Type Classes в Haskell.


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

НЛО прилетело и опубликовало эту надпись здесь

Тут про полиморфизм речь шла, а не про наследование.

Пример полиморфизма без ООП это перегружаемые функции и операторы, да, это статический полиморфизм и он резолвится компилятором в процессе сборки, но почему бы и нет? Вы можете, конечно, возродить что это C++ так умеет, но не чистый С, но именно с ООП это механизм не связан никак, просто возможность компилятора. Шаблоны (template), наверное, тоже можно назвать полиморфизмом и они тоже слабо связаны с ООП. Ну и если вся разница только в типах, то некое подобие перегрузки можно сделать и на чистом С макросами типа #define SUM(a,b) a+b
кроме указателей на функции в структуре, есть еще рефлексии (ака getMapOfMethods(объект) ) и селекторы\посылка сообщений (callMethodByName(объект, имя, параметры) — как например postMessage в WINAPI).

PS: Я не могу говорить за А.Кея, но ИМХО это был способ «помирить»
подход императивщиков, когда "все — данные" (с одной стороны это так и есть, ведь даже без «гарвардской архитектуры», на уровне железа все равно будет что число — это число, указатель — это число, строка — это несколько чисел, дерево — это несколько чисел, и в итоге функция — это тоже числа),
и функциональщиков, у которых "все — функции" (и даже числа они строят из лямбда функций).

И, хотя на практике различают объекты от например модулей (например для удобства хранения\обновления\загрузки и т.д.), но теоретические построения у них одинаковые: реализация скрыта, состояние напрямую недоступно, но оно хранится в памяти и мутабельно — общайтесь через интерфейс.
Главное в ООП — снижение количество элементов, которыми необходимо оперерировать для создания чего-то. Так в процедурном программировании мы имеем множество процедур и множество переменных разной степени глобальности. Всё это составляет элементы, с которыми необходимо считаться и держать их в голове (как минимум, чтобы не переопределить важную глобальную переменную). Это грузит мозг, и не даёт ему достаточно ресурсов, чтобы как-то оперировать всем этим богатством для достижения цели. Поэтому введены объекты как нечто, что полностью хранит своё состояние и предоставляет понятные методы для оперирования им. Тем самым из тучи методов и переменных мы имеем один объект, которым мозг оперирует как одной единицей. Именно поэтому можно на ООП писать весь тот софт. Не нужно иметь большую голову, способную разложить 100500 переменных и процедур, и при этом сохранить возможность описывать через них объекты, которыми оперирует приложение, да ещё и понимать как это всё нужно задействовать, чтобы данное состояние приобрело необходимый вид. Для такого уровня контроля необходима серьёзная подготовка либо талант. ООП даёт возможность менее прокаченному в базовых навыках специалисту писать приложения с множеством программных объектов. Всё остальное лишь вытекающие из этого плюсы. Полиморфизм ничуть не важнее инкапсуляции и наследования, всё это стороны одной медали.
Менее прокачанный в базовых навыках накидает глобальных переменных, положит методы не по тем классам, где надо, применит наследование там, где нужна композиция, сдобрит все это «самодокументирующимся кодом», а потом, когда придет заказчик и скажет «сегодня нужна фича Х», окажется, что для этой фичи нужен двухнедельный рефакторинг.
Более прокачанный и в процедурном стиле прекрасно напишет код без глобальных переменных, с идемпотентными процедурами и внятным интерфейсом.

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

З.Ы. про процедурный код с идемпотентными процедурами и без глобальных переменных: «Гладко было на бумаге, да забыли про овраги...»(с). Хороша теория, прекрасны подходы, да вот не всякая задача ложится в чистую и незамутнённую теорию и подходы, а вовремя остановиться и сказать, что вот тут нужно применить другой подход и иные инструменты, нужна не только большая голова но и серьёзный дан в разработке. А написать дрянь можно на всех языках, и процедурщину рефакторить тоже удовольствие спорное. Запутанную процедурщину рефакторить вообще талант Шерлока Холмса нужен.

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

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


  1. Любое программирование предполагает представление данных в структуры. Для обработки структур используются методы. Удобно структуры и методы для их обработки располагать в одном модуле, другими словами — это организация текста.
  2. Иерархичное расположение структур позволяет сократить описание как структур, так и методов.
    Таким образом, инкапсуляция и наследование не несет особых объектных принципов — можно сложить все в один монолитный модуль, а можно разложить по разным мелким модулям.
  3. Полиморфизм предполагает подняться над объектами, создавая полиморфные методы
    Это и есть программирование, как описание поведения объектов в ходе выполнения
Удобно структуры и методы для их обработки располагать в одном модуле

Лишь до тех пор, пока вы не начали писать хоть что-то нетривиальное. Когда одна и та же структура в разных контекстах обладает совершенно разным поведением, пихать все методы в один файл становится преступлением. В лучшем случае вы такие структуры продублируете аля Bounded Context, в худшем — получите классический God Object аля Active Record.
Если бы вы изначально проектировали от поведения, а не от структур, таких проблем бы даже не возникло. Но в этом случае вам бы даже в страшном сне не приснилось структуры и методы для их обработки надо располагать в одном модуле.


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

Сокращение описания структур — так себе цель. Во-первых, если у вас в структуре 30+ полей, а вы ещё от неё и наследуете, и полей добавляете — это уже плохо пахнет. Лучше подумать над доменной моделью, чем экономить десяток строк.

не понял — к чему замечания — Я написал
"Таким образом, инкапсуляция и наследование не несет особых объектных принципов — можно сложить все в один монолитный модуль, а можно разложить по разным мелким модулям."
Вы написали тоже самое другими словами

Проблема ещё что полиморфизм, есть не только в ООП. Мультиметоды много где есть.

И это опровергает, что полиморфизм — главное в ООП?

А полиморфизм для чего? Для упрощения же. Нет?

Нет — полиморфизм для реализации общих методов для разных объектов (разных по структуре).
Согласно исходной идее объекты существуют в среде сообщений — каждый объект обрабатывает одно и тоже сообщение (скажем, вызов метода) по своему


То есть суть не в простоте, а выразительности кода — одним полиморфным методом охватывается управление различными объектами

каждый объект обрабатывает одно и тоже сообщение (скажем, вызов метода) по своему

То есть суть не в простоте, а выразительности кода — одним полиморфным методом охватывается управление различными объектами


И? А для чего всё это? Это и есть упрощение.

Если вы считаете это простотой, трудно оспаривать — удачных упрощений

я слышал легенду (действительно не уверен, что это правда), что ООП был создан, чтобы можно было понятно интерпретировать бизнес-процессы в код. Ну, чтобы объектам реального мира соответствовали объекты кода с должным уровнем асбтракции. И взаимодействовали они между собой, как объекты реального мира с должным уровнем абстракции.
Но это не точно, было бы интересно почитать на эту тему.
я слышал легенду (действительно не уверен, что это правда), что ООП был создан, чтобы можно было понятно интерпретировать бизнес-процессы в код. Ну, чтобы объектам реального мира соответствовали объекты кода с должным уровнем асбтракции. И взаимодействовали они между собой, как объекты реального мира с должным уровнем абстракции.

Не было такого )

Но это не точно, было бы интересно почитать на эту тему.

Доклад Алана Кея, очень хороший — www.youtube.com/watch?v=fhOHn9TClXY&list=LLd6OFj5xQf9ZhwBb4EVbdSw&index=33

Пара его писем userpage.fu-berlin.de/~ram/pub/pub_jf47ht81Ht/doc_kay_oop_en
lists.squeakfoundation.org/pipermail/squeak-dev/1998-October/017019.html

Алан Кей, если что и ввел в своё время термин ООП

Вообще-то этим занимается UML

Нет, вы уж давайте тоже свой ответ оформляйте отдельной статьей! Здесь вам не тут, комментарии писать!

Процедуры и библиотеки с повторно используемым кодом были ещё в ассемблере в 60е если не раньше. А о том, что удобно делать изолированные «кубики» и из них собирать механизмы люди додумались ещё в Древнем Египте, за 5000 лет до ООП.


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


Далее утверждается, что это удобно. Правда не уточняется кому и для чего это удобно. Вроде как все должно быть очевидно. Новация очевидна, простота на лицо, всем идеи нравятся. В итоге имеем то, что имеем.

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

Это же называется абстракция. Строго говоря, это не уникальная фича ООП (как и вообще программирования), этот принцип успешно применялся и раньше.
Мне больше интересно, а какие реальные альтернативы ООП для энтерпрайза предлагаются?
Как правило, выбирается инструмент, а затем уже нужно смотреть, какие парадигмы инструмент позволяет использовать наиболее просто и быстро. Если это Java, то тут обычно без вариантов.

DI, иммутабельность, монады

DI и иммутабельность совершенно не противоречят ООП.

ООП ничего не противоречит, включая ФП и процедурное программирование. Речь о "классическом" ооп — объекты с мутабельным состоянием, инкапсулирующие в себе поведение.

А зачем себя ограничивать? Это не ООПшники на каждом углу с пеной у рта кричат: «ааа, ндао отказаться от ООП, ООП — отстой, ключевое слово класс пишут только мерзкие джависты» и т.п.
не надо отказываться от ООП, достаточно знать его сильные и слабые стороны, и применять с умом.

«ключевое слово класс пишут только мерзкие джависты» — не только они, например мне оно больше всего нравится у питонистов.
Мне больше интересно, а какие реальные альтернативы ООП для энтерпрайза предлагаются?

Прекратить искать подход который разом решит все проблемы энтерпрайза.

Дело в том, что класс/объект никогда не должен строиться вокруг данных, он должен строиться вокруг поведения — одной определённой ответственности. Простейшее утверждение! При этом одно оно позволит избежать практически всех антипаттернов ООП. А большинство паттернов — просто следствия из него.


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

Добавил секцию со ссылками и комментариями в свой пост. [2] — это моя статья про микс парадигм с точки зрения решения проблем, связанных с многопоточным программированием.
Это не так просто, а часто и невозможно. Далеко не всегда речь идет об OLTP, т.е. обработке взаимодействий, происходящих прямо сейчас — очень часто требуется анализ данных, полученных не в текущей сессии и накопленных в каком-то хранилище.
Да и для «поведения» классическая модель вызова методов не идеальна. Например (по мотивам первой статьи) — если у вас есть целая битва, в которой герои колотят друг друга, вам придется применять мультипоточность и бороться с ее побочными эффектами. В модели «очереди сообщений», например — такого не происходит.

Так в этом и дело, что когда объекты общаются посредством отсылки сообщений (как это в определении ООП было заложено), то у каждого объекта есть своя «очередь сообщений».
Проблема что по факту это реализовано только в модели акторов. А ООЯП на классах и вызовах методов изначально свернули не туда. В результате знаменитые холивары на темы типа "принтер печатает строку" vs "строка печатается на принтере".


Это не так просто, а часто и невозможно.

Я бы сказал, что это просто непривычно.


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

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

В результате знаменитые холивары на темы типа «принтер печатает строку» vs «строка печатается на принтере».

Значит ли это, что обьекты с методами никогда не содержат состояние? Те же акторы держат в себе данные.

Нет, не значит. Иметь состояние — это нормально и для актора и для объекта. А вот что именно будет его состоянием зависит исключительно от роли, которую он выполняет.
Другими словами, не "данные и методы работы с ними", а "методы, реализующие определенную ответственность, и данные, необходимые для выполнения этой ответственности".


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

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

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

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


И будет "пользователь хочет распечатать какую-то сроку на каком-то принтере, для чего менеджер определяет какую и на каком и сообщает о желании пользователя объекту, представляющему принтер"

«настоящим» обьектам, представляющим данные

Почитайте ветку чуть выше. Я начал как раз с того, что настоящие объекты не должны представлять данные. И им не нужны вспомогательные объекты, им нужен интерфейс для получения нужных данных. А кто там выступит в роли провайдера этих данных, будет ли он один или несколько, не так уж важно.
По сути любая программа — это тупо набор конвейерных линий по обработке данных. Вы получаете пользовательский ввод, как-то его обрабатываете, в результате этой обработки что-то идёт на склад (в БД), а что-то отдаётся обратно пользователю.
А объекты — это рабочие на этих конвейерах, к ним приходят одни данные — уходят другие (обработанные) и отправляются к следующим объектам.

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

А какое из определений ООП вы считаете классическим?


P.S. Мне кажется, с ответа на этот вопрос должна любая статья про ООП начинаться :-)

То, в котором обьекты представляют сущности из области определения, а методы — взаимодействия между ними.

Про такое я что-то не слышал… можно целиком его привести? А то непонятно, что такое "сущности из области определения".
Да и метод, вроде как, не может представлять взаимодействие между объектами, т.к. он принадлежит одному из объектов. Другими словами, метод максимум может быть частью интерфейса возможного воздействия на объект (однако, это однонаправленное понятие, в отличии от взаимодействия). Но и то публичные методы — необязательная часть, т.к. объект может и активную "жизненную позицию" занимать и сам откуда-нибудь брать себе работу (из очереди например), без внешнего обращения к нему.

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

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

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


А классическое определение ООП, если что, выглядит так:


  1. Everything is an object.
  2. Objects communicate by sending and receiving messages (in terms of objects).
  3. Objects have their own memory (in terms of objects).

Про другие определения можно применять эпитеты "популярное", "распространённое" и т.д. Но классическое есть только одно.

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

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

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

Допустим у вас есть N=200 алгоритмов преобразования дерева и M=100 типов узлов. Эти N и M независимы. Каждый из N должен делать специфические действия для каждого из N.

Если вы структурируете код без OOP, у вас универсальная функция обхода дерева + один тип узла Node + N=200 функций для локального преобразования дерева типа:

transformation_1 (Node * & rp)
{
switch (rp -> op)
{
case OP_CONSTANT: // Делай что-то для константы
case OP_PLUS: // Делай что-то для PLUS

default: // Ничего или по умолчанию
}
}

transformation_2 (Node * & rp)


Если же вы решили сделать иерархию типов узлов, например:

class NodePlus: Node {… }
class NodeConstant: Node {… }

— и при этом сделать все 200 преобразований деревьев виртуальными функциями, то у вас будет N * M = 20000 маленьких виртуальных функций типа:

NodePlus::transormation_1 ()
NodePlus::transormation_2 ()

NodeConstant::transormation_1 ()
NodeConstant::transormation_2 ()


Как вы будете группировать эти функции по файлам? Группировать по алгоритмам или по классам? Если по алгоритмам, то вы раскидаете один класс по 200 файлам, что просто увеличивает количество набитого вами кода и никак не помогает уменьшить сложность. Если по классам, то оно резко увеличивает сложность, так как теперь, чтобы понять каждый алгоритм, программисту нужно будет просматривать не один, а 100 файлов.

OOP годится для определенных программ, например для GUI. В других программах он приводит к тому, что программисты занимаются не полезной работой, а борьбой за/против OOP, из-за чего получаются непонятные, большие, медленные программы.

Disclaimer: я использую C++ и OOP с 1993 года, и пришел к выводу о необходимости использовать OOP сдержанно после года через три после начала его использования. Желание наплодить классов и иерархий — это желание либо новичка, либо человека, которые пишет GUI или подобные легко укладывающиеся в классы приложения. Весь софтвер, которые делает сложную работу с графами — just say NO to OOP.
Объясняю на пальцах, почему такие рассуждения ничего не доказывают. Допустим, у вас есть бревно.

Если у вас есть топор, то у вас есть просто одно движение, типа:

«хрясь»

Если же вы решили воспользоваться ножом, то вы можете, например:

«пилить»

И при этом будете делать все 200 перепиливаний ножом.

Как вы будете пилить 200 бревен? Группировать кучками? Или раскидаете?

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

Disclaimer: я использую нож и топор с 1993 года, и пришел к выводу о необходимости использовать нож сдержанно после года через три после начала его использования. Желание наплодить картофелин — это желание либо новичка, либо человека, который любит жареную картошку или то, что похоже на жареную картошку. Все, кто имеет дело с бревнами — скажи нет ножу, даже если это ювелирная резьба по дереву.
А ООП запрещает создать функцию `visitAll() { for(node: nodes) node.do(); }`, где do() перегружена для разных типов узлов (или то же самое с переданным callback)?
Ну и в моём представлении ООП — это надстройка над процедурным подходом, разрешающая дополнительные действия, но не запрещающая процедурные.
Я возможно не совсем четко описал суть проблемы. Я уже использовал callbacks в случае, который я описал.

Каждый алгоритм делает операцию не над одним узлом, а над поддеревом дерева. Т.е. общая функция visitAll обходит дерево и потом делает операцию не над текущим узлом, а над узлом и его соседями. Например один алгоритм при обходе заменяет все *p = *p + 1 на (*p)++ и *p = *p — 1 на (*p)--, а другой f (*a = b) на *a = b; f (b).

Т.е. ему нужно смотреть не только на node, но и на node.left, node.left.left, node.right, node.right.left и node.right.right. А потом переходить к другому узлу и тоже смотреть на соседей.

А теперь перефразирую свой коммент:

Так как алгоритмов 200, а типов узлов 100, и каждый алгоритм должен поддерживать все 100 типов узлов, то перегруженных функций «do» будет очень много, вплоть до 20000, так как в каждом из 100 классов узлов будет до 200 функций do_XXX (хотя большинство таких функций будут пустыми (т.е. можно использовать функцию base класса), но и непустых будет много.

Как расбросать эти много функций по файлам, чтобы сделать код читабельным? Группировать по алгоритмам или по классам? Если по алгоритмам, то вы раскидаете один класс по 200 файлам, что просто увеличивает количество набитого вами кода и никак не помогает уменьшить сложность. Если по классам, то оно резко увеличивает сложность, так как теперь, чтобы понять каждый алгоритм, программисту нужно будет просматривать не один, а 100 файлов.
Каждый алгоритм делает операцию не над одним узлом, а над поддеревом дерева. Т.е. общая функция visitAll обходит дерево и потом делает операцию не над текущим узлом, а над узлом и его соседями.…
Т.е. ему нужно смотреть не только на node, но и на node.left, node.left.left, node.right, node.right.left и node.right.right. А потом переходить к другому узлу и тоже смотреть на соседей.

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

Как расбросать эти много функций по файлам, чтобы сделать код читабельным?

YuriPanchul, у вас ошибка на уровне проектирования.
А если бы было не 200 * 100, а 200 * 300 * 400?

Такие вещи не пишутся и не поддерживаются «вручную».
Поэтому вопрос о том, как и что тут группировать в какие файлы излишен.

хотя большинство таких функций будут пустыми


Явный указатель на то, что «что-то идёт не так и пытаться описать и поддерживать это вручную на базе ООП-модели, да еще и зачем-то отдельными файлами ошибочно.

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

ООП это не:

ClassForGraphNodeOne
ClassForGraphNodeTwo
ClassForGraphNodeThree
ClassForGraphNodeFour

ClassForGraphNodeThousand // Тысячный класс для тысячного типа ноды.

Я встречал такое применение ООП и это ошибка.
Не надо лепить статическую (под)типизацию классами языка.
Классы/объекты предназначены не для этого.

ООП это:

NodeClass {
field_for_node_type // here one, two, three, etc…
}
то есть один класс для ноды (она же point, она же vertex)

EdgeClass {
field_list_for_nodes
}
второй класс для рёбер (они же линки, стрелки или дуги)

Весь софтвер, которые делает сложную работу с графами — just say NO to OOP.


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

Вашу исходную задачу из первого поста выше нужно пытаться решать или тектовой кодогенерацией, или же выстраивать механизм обработки динамически, в памяти.
Но плодить пересечения из сотен классов и сотен методов и писать это руками — не надо так программировать.
Дык это делаю не я. Я видел это в трех коммерческих проектах и слышал предложение так делать (якобы «более объектно ориентированно») от некоторых программистов. Прежде чем прийти к выводу «нужно два класса» всякие ярые новички ООП начинают выделывать кучи классов.

И не только в парсерах и компиляторах. Я видел трех студентов с горящими от ООП глазами, которые написали объектно-ориентированный cycle-accurate симулятор процессора, в котором было на порядок больше кода, чем надо, а при изменении конвейера массажировать всю иерархию классов было невозможно. Студенты из Беркли пришли кстати.

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

И вот если эти полгода приходятся на начало коммерческого проекта, а не пробно-студенческого, то проекту швах.
Если не вы, тогда прошу извинения. (У самого ошибка — не смог выдать нейтральный, безличностный текст).

В целом же да, вы совершенно правы. ООП и базовая литература о нем толкает начинающих делать бессмысленные иерархии классов и много других бессмысленных вещей (например класс — структуру данных и десятки getter-setter методов вместо того чтобы безбоязненно создать struct).
К этому добавляется юношеский запал, энергию девать некуда. Плюс, для многих это составляет своего рода занятную задачу, описать всё классами, а потому их надо наплодить как можно больше и некоторым явно доставляет удовольствие заниматься «классотворчеством».

Только полгода слишком оптимистично. Обычно больше, года 3, а то и 5-7 лет прежде чем получается нормальный разраб с умеренными представляниями проектирования. А всё потому что изучить механизмы ООП — этого мало. Надо научиться их грамотно применять, структурировать проект, а это уже уровень выше — уровень проектирования, а до него надо еще суметь добраться. Молодым на нём обычно скучно, «зачем читать эту скукоту, я и сам смогу!» :)

И вот если эти полгода приходятся на начало коммерческого проекта, а не пробно-студенческого, то проекту швах.
Совершенно верно. Надо все-таки чтобы хотя бы «на бумаге» структуру накидал дядька постарше, а студни-аспиранты пусть заполнят предолженный каркас. Иначе действительно, добра не жди, в этом я с вами совершенно согласен.

Вот и получается что и тут всё как везде, без изменений. Хоть это будет software development «фабрика», или это будет завод по выпуску токарных изделий или автомастерская. Внезапно, еще одно знание, уже уровня людской организации. Как на старом добром призводстве электроники, нужен «мастер» и вокруг него подмастерья. Нужен «начальник цеха» и «старший инженер». И это только производственная часть.
Нужен генеральный конструктор и к нему- инженеры-разработчики. Нужны наладчики. Да и отдел контроля (сиречь тестирования) внезапно не помешают. Вот тогда, считаю, будет толк. А набить десятки комнатух одними только подмастерьями, как сейчас делают… Результат будет ожидаемым, спасает только то что продукция годами допиливается до новых версий.

Если ООП у вас увеличивает сложность — вы неправильно его используете (конкретнее: используете там, где не надо).


OOP годится для определенных программ, например для GUI. В других программах он приводит к тому, что программисты занимаются не полезной работой, а борьбой за/против OOP, из-за чего получаются непонятные, большие, медленные программы.

Вы использовали какие-нибудь сторонние библиотеки на C, кроме GUI? Даже мало-мальски сложные из них (zlib, libcrypto, lua) имеют ООП-подобные API, где создаются какие-то объекты, и для них вызываются методы. Даже в библиотеке apache commons math ООП там, где оно используется (а это не везде, у них довольно разумный подход), выглядит уместно.


Если по классам, то оно резко увеличивает сложность, так как теперь, чтобы понять каждый алгоритм, программисту нужно будет просматривать не один, а 100 файлов.

Не лучше ли просмотреть документацию, чем реализацию алгоритма? Если вы каждый раз смотрите реализацию алгоритма и у вас всё нормально — у вас не слишком сложная система по сравнению с другими, где без ООП действительно не обойтись.


Весь софтвер, которые делает сложную работу с графами — just say NO to OOP.

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

Следующее

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

Вы обвиняете автора в выдумывании понятия а далее сами выдумаваете ещё одно, вместо того чтобы обратиться к первоисточникам.

Алан Кей происторию создания ООП, и пара его писем, если интересно:
userpage.fu-berlin.de/~ram/pub/pub_jf47ht81Ht/doc_kay_oop_en
lists.squeakfoundation.org/pipermail/squeak-dev/1998-October/017019.html
Вот именно.

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

Это да, согласен что одна из причин такого искажения понятия — отсутствие четкого определения и примеров даже от его автора, ну и ещё он писал об этом:
If you focus on just messaging — and realize that a good metasystem can
late bind the various 2nd level architectures used in objects — then much
of the language-, UI-, and OS based discussions on this thread are really
quite moot. This was why I complained at the last OOPSLA that — whereas at
PARC we changed Smalltalk constantly, treating it always as a work in
progress — when ST hit the larger world, it was pretty much taken as
«something just to be learned», as though it were Pascal or Algol.
Smalltalk-80 never really was mutated into the next better versions of OOP.
Given the current low state of programming in general, I think this is a
real mistake.

И
Smalltalk is not only NOT its syntax or the class
library, it is not even about classes. I'm sorry that I long ago coined the
term «objects» for this topic because it gets many people to focus on the
lesser idea.

The big idea is «messaging»


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

Думаю основная проблема по прежнему в людях, которые стали использовать «модное» понятие чтобы продвинуть себя/свой продукт, а после пытаться найти в нем серебрянную пулю, решение от всех проблем, пихать его везде, куда можно, при этом даже не удосуживаясь разобраться что это такое и зачем нужно. И проблема это существует с любыми популярными понятиями, люди пытаются запихнуть в свой код паттерны, пытаться сделать «всё по SOLID», «по MVC», даже не задумываясь о конкретном влиянии их действий на качество, в данном случае, кода, и реальный смысл и идеи популярных терминов.

Как пример явного искажения термина, имеющего достаточно четкое и понятное определение — REST, было описано что это архитектурный стиль, набор ограничений при разработке клиент-серверных приложений, решающий определенные проблемы.
Что мы видим сейчас — люди кличут REST любую http апишку, которая, чаще всего, просто принимает и отдаёт json, и выполняет процедуры по запросы. Статья на хабре, Дока спринга, куча вопросов на тостере, S/O, более поздних статей на хабре тому подтверждение.
Даже попытка автора термина, Роя Филдинга, напомнить людям что то что любая http апишка не является REST успехом, вобщем то, не увенчалась — roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven, мода ведь, синдром утенка проявляющийся у новичков, которые уже «выучили» что такое REST по приведенным выше стаье с хабра/spring.io и подобных

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


Основное в каждой парадигме — это абстракция моделирования, а не приемы и организация кода.


То есть, если у меня есть персонаж в игре, который умеет что-то делать, то это ООП, он хранит свои данные и умеет выполнять какие-то действия, которые основаны на этих данных и на входящих параметрах (или сообщениях, если кому-то принципиально смотреть на это именно так).


Но если у нас есть MVC в вебе в каком-то его виде, то Контроллер — это просто группа методов, и то, что они представлены классом — это просто организация кода.
Вот уже в Модель можно что-то моделировать на основе ООП, если в этом есть смысл.
Но объективно, в большей части ситуаций действующее лицо — это "Deus ex machina", который уже управляет группой связанных объектов, который мы обычно и называем уровнем Сервиса, тогда мы приходим к тому, что данные отдельно, функции отдельно и это уже не ООП.

Итак, главное в программировании — получение продукта который удовлетворяет требованиям заказчика.
Когда я собираю себе рабочее окружение предпочитаю софтинки написанные на 'plane C' таковым написанным на C++ при прочих равных. В графическом интерфейсе GTK предпочитаю QT. То есть с потребительской точки зрения мне импонирует когда умные люди победили сложность минимумом абстракций и изоляций.) Наверное это субьективное, но из моего опыта софт на C обычно быстрее, легче и стабильней. И мне больше нравится его рихтовать под себя и я обычно при этом учусь чему то новому.
Я не знаю кто минусует. Наверное, те, кто ядро Линукса на С++ переписывают в свободное от школы время)
Я лично полностью согласен с автором. «Плоская» структура С-кода даёт гораздо больше свободы и возможностей, чем С++ или Java аналоги.

Ядро Линукса, кстати, несмотря на используемый язык насквозь объектно-ориентированно.

… limiting your project to C means that people don’t screw that up, and also means that you get a lot of programmers that do actually understand low-level issues and don’t screw things up with any idiotic “object model” crap.


Лично мне в этот цитате Линуса сложно заметить любовь к ООП. Конечно, некоторые адепты ООП могут искать в ядре Линукса следы «сквозного ООП», но как мы знаем при желании и при наличии определённой доли фантазии можно найти что угодно и где угодно. А где подтверждение любви к ООП и его использования в Linux Kernel от самого автора?

Не знаю насчёт фантазий и любви, но как вы думаете, откуда взялось название структуры struct kobject и почему она так часто используется?

Да откуда угодно могло взяться это название. Я предпочитаю не гадать и не придумывать интерпретации. Если Линус скажет прямо, что он использует ООП в ядре Линукса, я поверю. По умолчанию — не верю. Если кто-то другой про это скажет — тоже не поверю, уж простите меня великодушно.

А я предпочитаю смотреть код, а не читать чужие мнения...

Ну в данном случае как раз Ваше мнение про этот код и будет чужим. Родной для Линукса — только Линус Торвальдс. По праву создателя)

Когда я стал читать книги по паттернам, то оказалось, что к некоторым из них я сам пришёл задолго до чтения. И авторы сборников паттернов, глядя на мой код, вполне могли сказать "да, это ещё один пример этого паттерна, только названия не как у нас".


Я к тому, что к вопросу о том является ли Линукс примером ООП надо обращаться к экспертам по ООП. Может Линус и не подозревает, что "всю жизнь разговаривал прозой", а может подозревает, но не хочет это принимать :)

Я думаю, у нас разный взгляд на оценку произведений творчества.
Например, я написал код, где встречаются элементы ООП, элементы ФП и какие-то общеизвестные паттерны. Но я назвал это MyVeryOwnParadigm.
Вопрос — корректно ли мне навязывать мнения типа «да ты просто изобрёл велосипед, это тот же ООП»?
Лично я считаю, что автор сам в праве определять как называть и классифицировать его творение. Если он не хочет, чтобы его OOP-like-Paradigm называли ООП, то зачем так делать?

Вам навязывать некорректно, но приводить другим ваш код в качестве оригинальной интерпретации идей ООП — почему нет?

Ну потому что это будет неавторитетно. Я могу влезть в такое обсуждение и сказать, что мой код — не ООП) И все совпадения с реальными паттернами — случайны.

Даже если этот пример будет приводить автор ООП это будет не авторитетно? :)


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

Строгого определения что же такое ООП — нет. Поэтому спор про ООП сразу переходит в типичный спор «физиков» и «лириков»:
физики: мы пишем на C#, у нас контроллеры — просто мешок процедур, а на выход-выход — голые DTO
лирики: ну так это ООП, просто вы не используете полиморфизм и инкапсуляцию. А если бы использовали — у вас было бы все сильно лучше
физики: вот в Haskell никакого ООП нет, и живут люди
лирики: тип данных — это инкапсуляция, классы типов — полиморфизм, вы пишете на ООП

Ну и так далее.

У меня вот в коллективе, термин «ООП» вообще несёт смысл «малолетние дурачки придумали архитектуру». Ну типа «я глянул к ним в код, там полный ООП» — значит код раздули кучей абстракций, не несущих никакой пользы.
Это классическая реализация ООП, не надо удивляться)
ООП понимают не только лишь все


image

Ну, скучно же, ребята!!! Один пишет про ошибки, допущенные другим в статье про ошибки третьих… спор о том, кто больше неправ продолжился в комментариях. Ну неправ, и что? Лучше бы написали о каком-нибудь крутом и красивом решении, которое показывает как XXX парадигма расширяет наш инструментарий, да с примерами, да так чтобы захотелось попробовать! А то "тут ООП, тут не ООП..." Как говорил Жванецкий: "Воруйте с прибылей!"

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

см. [2]

Так это же называется модульность, а не ООП?

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

Почему! Почему? С изоляцией сложности, стало писать сложнее? Требуется больше теорий и прочего. Или это сформировавшийся стереотип вокруг ООП, что для ООП нужно знать ООП, а не только как оно ложиться, обертывая обычное функциональное программирование и всю ту же теорию?
:)

Без изоляции сложности сложно написать сложную программу. :)

Куда из твоего предложение девалось ООП? Или с ним все таки сложнее делать изоляцию сложности? Новая парадигма! Лейкопластырь! И все станет просто! Изолируй ООП :)

Основная, наверное, цель ООП — путём изоляции сложности упростить написание сложных программ.


И, кстати, лично я активно использую смесь процедурного и ОО программирования: точка входа — скрипт, который создаёт небольшой (для уровня скрипта) набор объектов и дёргает их методы, не "подозревая", что за ними скрываются графы объектов из тысяч вершин. Это изоляция ООП? :) На некоторых ООП языках сделать такого не получится и придётся оборачивать подобные процедуры в, например, классы-обёртки со статическими методами.

И какой в конечном счете основной инструмент упрощения сложностей — ООП со своими сложностями, функционал со своими(такими же), или все таки изоляция сама по себе? Которая и привела к революции. Или так всю жизнь и было, что упрощая мы открывали что то новое? А то глядишь с такими формулировками, без ООП ни какого прорыва никогда нигде и не было.

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

Уж взялся писать, так пиши нормально. Сейчас это просто комментарий.

… всю статью можно перечеркнуть буквально следующим.

Следующее
(непереводимая игра слов)


Это фиаско, бро. Тут даже обсуждать нечего… А я так надеялся (
Небольшая статья породила очередной холивар в мире ООП? Истина где-то рядом?
Здесь даже обсуждать нечего. Я даже не знаю, к чему придраться. Я ожидал здесь увидеть хотя бы адекватные аргументы, почему там написан бред, но увидел лишь очередные философские размышления «на тему», без конкретных примеров.
То, во что превратилось ООП сегодня, является следствием непонимания целей разработки софта: борьба со сложностью.

А мне казалось, что целью разработки ПО является не борьба со сложностью, а решение реальных задач. И часто сложные задачи для их решения требуют, увы, сложного ПО.

Высокая сложность и есть проблема возникающая при разработке сложного ПО, поэтому управление сложностью и встаёт, по сути, на первое место, реализовать фичу А — не проблема. Реализовать фичу А в уже написанном и сложном приложении — проблема.
p.s. Речи про специфичные области где главное фичу реализовать, а код и переписать кучу раз можно, не идёт.

Хотел бы посмотреть на лучший ООП код автора статьи. gridem, поделитесь ссылкой?
Очевидно, что минус от автора, и он подсказывает мне, что весь код топикстартера представляет собой исключительно корпоративную и засекреченную тайну, под семью печатями NDA. Короче, показать его общественности ну никак не представляется возможным, а весь тот код, который можно показывать — исключительно старый, учебный, и его показывать стыдно. Но это ведь нормально, что все мы учились на своих ошибках? Слышал эту байку, но в нее не верю.
Хотел бы посмотреть на лучший ООП код автора статьи

Автор статьи должен специально для статьи какой-нибудь проект на несколько лет человеко-часов сделать чтобы доказать что в интернете кто-то не прав?

В этом нет особого смысла, потому что это будет лишь попытка что-то доказать людям которые не разбираются в вопросе исключительно из-за своего нежелания в нем разбираться, и если красивым и убедительным словцом навязать им новую парадигму, какой бы хорошей она не была, без усилий этих людей, писать лучший код они не станут.
Материалов то за пол века для изучения накопилось предостаточно.
Ну автор же выкинул эту статью, как корм голодным собакам на растерзание. Именно это он и сделал, как вы говорите, — и сказал он: «читайте книжки». Но я хочу понять именно его точку зрения, познать именно такое же просветление, которого добился он. Ведь он же зачем-то написал этот пост, изобилующий абстрактными фразами, на интерпретации которых может уйти еще полвека. Я хочу докопаться до истины — какое такое ООП он познал, что ему позволено, и не стыдно выпускать такую статью, являющуюся критикой без критики, аргументом без аргументации, сутью этого несуществования (или как там было?).
Автор не пишет что познал какое-то мифическое ООП, понимание которого разное у каждого из десятков комментаторов предыдущей статьи, автор, как мне показалось, хочет донести историю его создания, и имхо, лучшим способом будет направить людей к более авторитетным источникам, а не пересказывать уже имеющуюся в интернете информацию своими словами на хабр.

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

Ну и да, полностью согласен с автором, что спорить «X vs Y», или «X — хорошо, нет, или не очень?» не имея формального определения X бессмысленно, а именно так и выглядит большинство споров на текущий момент:
— «Я считаю что ООП не очень из-за наследования»
— «Но ведь в ООП не обязательно наследование»
— …
Ну такой я и сам нашел… Куча абстрактных телодвижений вокруг небольшого числа сущностей. Это конечно, не умаляет ваших достижений, но, к сожалению, понять красоту данного кода может не только лишь каждый: мало кто может это делать.
Допустим, я увидел прикольный завуалированный способ объявлять глобальные переменные. Конечно, все обвешано шаблонами, фиктивными параметрами, чтобы наплодить возможность реализации одной хрени для разных типов. Но в какой момент эта штука возьмет и начнет давать преимущество огромному проекту, в котором десяток программистов и не только лишь все такого же уровня, как я, и мало кто такого, как Вы? И конечно, этот синглтон станут использовать сначала правильно — пока Вы смотрите, но как только Вы отворачиваетесь — его начинают использовать для любой хрени. Глобальные переменные же плохо! Давайте спрячем их, завуалируем и сделаем так, что хрен пойми когда они будут инициализироваться. Кто первый дернул за веревочку, тот и прав. Пусть еще это будет размазано по коду, запрятано в библиотеки. Постепенно, проект выйдет из под Вашего контроля и черти в аду перекрестятся, когда им предложат этот код для в качестве орудия пыток программистов-грешников. А между тем, мы обсудили всего несколько строчек такой «архитектуры». И даже не начали обсуждать ООП, к которому, я уверен, в таком простом проекте у меня не будет претензий. Но все мы знаем, что в действительности все не так, как на самом деле.
Я бы предпочел увидеть некоторый реально работающий проект автора, а не маленькую библиотеку, которая по смыслу оторвана, изолирована, а значит и проще в реализации, чем реальные проекты. Скажем, вот один из моих. Я им не горжусь, но все же там есть кое-какая архитектура, пусть даже все и обвешано шаблонами (куда ж без них?).
Зарегистрируйтесь на Хабре , чтобы оставить комментарий

Публикации