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

Объектно-ориентированное программирование – катастрофа за триллион долларов. Часть 1

Время на прочтение3 мин
Количество просмотров11K
Всего голосов 48: ↑8 и ↓40-32
Комментарии89

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

Пока ни ru_vds, ни mailru не выкатили свои переводы этой статьи, я решил выложить свой вольный пересказ ее начала. Посмотрим, что из этого выйдет.
Статье больше года. Поздняк спешить =)

Они не переводили эту статью, ибо это дно, а не статья.

Здесь и сейчас не принято критиковать ООП. Попробуйте вернуться к теме через пару лет.

Просто надо разделять критику ООП как такового и его реализации в конкретном ЯП.

И критику как его используют в конкретных приложениях

НЛО прилетело и опубликовало эту надпись здесь
Ну, вот мы и увидели, что из этого вышло.
НЛО прилетело и опубликовало эту надпись здесь
Да было уже несколько холиваров на эту же практически тему. И если прочесть оригинал, то в общем видно, что никаких особо новых мыслей мы там не найдем. Все уже изложено, и не по разу.
НЛО прилетело и опубликовало эту надпись здесь
Последнее выражение, по идее, должно всегда выдавать 4

Да с чего бы? А я вот говорю, что 2+2 должно всегда выдавать 22, ну или в крайнем случае 13. Кто вам, собственно, сказал, что сложение в десятеричной системе?
Я бы сказал, что и оригинальная статья несколько спорная. Ну а перевести из нее пару страниц и остановиться… ну это автоматически означает опустить все аргументы за и против. А что такое мнение без аргументов? Даже как повод для холивара — и то мало пригодно.

Статья фигня, а вот похожие статьи, которые выдал хабр заинтересовали.

Вообще автор оригинала пишет про себя так: ilya@suzdalnitski.com Senior full-stack engineer. Guides teams to building reliable software. Elixir/JS/Node/React.

А потом начинает критиковать Java и C#. Мягко говоря, критика инструментов, в которых автор даже сам не считает себя специалистом, редко когда выглядит убедительно.

Что важнее для программиста — писать надёжный или понятный код? Давайте посмотрим на условные экстремумы — не очень надёжный, но очень понятный код; и очень надёжный, но не очень понятный код.


deadline:


  • code0 works
  • code1 works. Sometime.

новая фича:


  • code0 works.
  • code1 works. Better than last time.

Новый программист реализует новую фичу:


  • code0 broken. No one can understand why.
  • code1 works, even better.

Насчёт последнего же — Но что будет, когда проект разрастется? OOP – это мина замедленного действия, которая неизбежно рванет, когда код проекта достигнет определенных размеров. Проекты начинают задерживаться, дедлайны срываться, разработчики падают без сил, а добавление новых фич становится практически невозможным.


Простите, это судьба любого большого проекта. Либо люди надрываются и проект живёт, либо отпускают и проект дохнет. OOP тут совершенно ни при чём, потому что миску лапши можно сделать даже из кода на haskell..


… Пожалуй, из кода на haskell можно сделать особо забористую миску лапши...

Вы уверены, что тут 0 и 1 в примерах не перепутаны?

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

Не. Это-то понятно. Но дело в том, что предыдущая фраза говорит:

>не очень надёжный, но очень понятный код; и очень надёжный, но не очень понятный код.

поэтому неявно «не очень надёжный, но очень понятный код» ассоциируется с code0, а code1 наоборот. То что вы их дальше перенумеровали — далеко не очевидно.

А, это да. Пардон.

Ну, в общем смысл-то я думаю все поняли, но поломать голову пришлось :)

А все дело в том, что изначально задан абсолютно не верный постулат о том, что программист должен писать "простой код". Ничего подобного! Программист должен писать код, обеспечивающий минимальную стоимость владения. Что означает, что код должен легко тестироваться, легко изменяться (читаемость кода входит именно сюда) и легко расширяться. Все. Больше программист никому ничего не должен.

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

А ещё лучше, удалять код для решения задач бизнеса :)

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

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

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

Внутри реакта, конечно, ООП, но снаружи этого не заметно.
Про иерархию GUI: обычно эта глубоко-построенная иерархия очень плохо влияет на размер исполняемого файла, так как тянет за собой чуть-ли ни весь возможный код этой иерархии. Это хорошо видно на примере размеров исполняемых файлов Delphi или кучи библиотечных файлов для для программ сделанных в визуал-студии.
А на Го это не заметно?

ООП нет, а бинарники вдесятеро больше =)

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

Кого в 2020 волнует размер бинарника? Ну кроме эмбедд устройств?

Тех, кому приходится рассылать обновления [по почте итп].

Да и размер он не просто так появляется, это же и время линковки, минутное уже напрягает.
девопсов.
Вы на всякий чих делаете гигабайтные бинарники, а нам потом делать CI/CD, которое должно смочь собрать билды скажем на 100 коммитов в день, потом еще их хранить какое-то время, потом билды которые кандидаты в релиз, но еще не релизные уже хранить полгода. И еще чтобы это все быстро копировалось и разворачивалось где-то в облаке. И вот уже проблема в том, что гигабит — не очень быстро.
Ну тут вопрос все-таки не в абсолютном размере бинаря, а в процентном соотношении. Навряд ли если бинарь на го весит гигабайт, переписанный на C он будет весить 10мб и решит эту проблему.
Вы меня немного заставили подзависнуть пытаясь представить что там за гигабайтные бинари в облаке разворачиваются. При таких объемах логики и функционала запиханного внутрь бизнесу уже надо понимать что гигабит действительно мало и думать в сторону 40/100G. Да и вообще гиговый монолит для девопса должен выглядеть немного дико, где вся микросервисная архитектура и все вот это.
PS. Извиняюсь, я действительно не мог представить что там можно все раздуть до таких неприличных размеров. У нас высоконагруженное серверное решение развернутое на сотнях bare-metal и обмолачивающее 40г трафа написано на GO и весит около 20метров, так что с такими проблемами с деплоем я не сталкивался.
Я уже рассказывал про подобный случай.
Был монолит. В дистрибутив были запихнуты 3rd party библиотеки.

По модной тенденции было принято идти в микросервисы. Монолит разделился на десятки микросервисов, но при этом КАЖДЫЙ тащит весь пакет 3rd party библиотек, вместо того чтобы выделить их в отдельный архив.

Повлиять на это сложно — команд разработчиков несколько, никто не хочет согласовывать версии зависимостей, системный архитектор был завален бизнес реквестами, разгребаться в этом не хотел. Так и жили.

Вот только не надо про раздутый размер бинарников Delphi. Они адекватны программам той сложности, для которых Delphi было спроектировано. То, что в качестве побочного эффекта получался 200кб бинарник у одного приложения из пустой формы — это побочный эффект. Кроме того, был способ уменьшить размеры, если рантайм ставить отдельно. Рантайм visualstudio либо идёт в комплекте к Винде, либо ставится при установке приложения, чем комплект bpl хуже?

Для простых утилит — побочный эффект великоват. Поэтому я предпочитаю вместо VCL библиотеку KOL с его практически одноуровневой иерархией и без виртуальных методов.
… А теперь сравните 200..300 Кб на Delphi/Freepascal с 2..5 МБ на Golang.
Лично я считаю, что не в ООП проблема, а в неверном подходе при проектировании продукта. Лично я считаю, что проект должен быть модульным, но при этом иерархии модулей не должны быть сильно глубокими. В этих зависимостях можно потеряться. ООП прекрасно для этого подходит. Зная назначение каждого модуля, искать причины багов гораздо проще.
Кстати, следующая статья автора оригинала называется: «Functional Programming? Don’t Even Bother, It’s a Silly Toy». Очень похоже на то, что человек просто специализируется на закидывании кака камнями парадигм, которые он не осилил.
Нет, там как раз ироничное название. И вся статья в целом — высмеивание аргументов со стороны «сторонников» ООП.
Просто помню что перевод был на хабре habr.com/ru/company/ruvds/blog/462483
Хмм, ладно. И все же это не отменяет того, что аргументация автора на уровне «во всем виноват Волан-де-Морт». Вся оригинальная статья по сути состоит из выдернутых из контекста цитат и обвинений ООП в неправильном понимании ООП автором.
Да любой, кто критикует ООП, просто его не понимает. Я вот не понимал, но меня научили опытные специалисты.

"Наш мозг развивался в сторону «делания чего-то», а не в сторону выстраивания сложной иерархии из абстрактных объектов"


Эм, так человек же социальное животное, и для выживания строил сложные социальные схемы в голове, когда ещё обезьяной был. Тот вожак, того не тронь, того почеши, с тем за ягодами сходить можно завтра.


То есть, как раз на обработку достаточно большого количества объектов и связей мозг заточен. Есть число Данбара, согласно с которым 150-200 обьектов и связей между ними человек в голове удерживает.

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

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

ООП явно увеличивает количество программистов и помогает компаниям их менять в случае чего.

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

OOP считается самым большим бриллиантом в короне компьютерной науки.

Кем? Где цитаты и рейтиниги всех бриллиантов?

Лучшим способом организации кода.

Где это сказано? Кем? В каком контексте?
И все остальное в том же ключе. Нигде не приведено источник, кто так утверждает причем утверждает именно в таких формулировках.

Главной целью любого разработчика является написания надежного кода.

Спорно.

А какой самый лучший способ написать надежный код? Сделать код простым.

То есть сперва кто-то утверждал что ООП самый лучший способ написать код. Теперь самый лучший способ — это написать простой код.
А если ТЗ сложное большое? А что такое простой код? каковы его характеристики?

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

Даже в fizzbuzz люди делают ошибки. А значит простой код — не самый лучший способ написать надежный код.
На самом деле надежность кода повышают тесты, в том числе и юнит тесты. Которые в ООП писать зачастую проще, чем в «простом неструктурированном коде».

Ибо моя цель не оскорбить, а указать на проблемы, которые есть в OOP.

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

Может, мне надо было научиться еще паре десятков «паттернов программирования»? В конце концов, у меня на всё это просто не осталось сил.

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

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

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

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

Если разработчик правильно осилил ООП, у него код будет ЛОГИЧНО делится на классы. Не будет суперклассов. не будет зависимостей, когда джуниор поправил метод и это зааффектило и твой продукт и всех зависимых сервисов незаметно для всех.
Потому что юнит тесты не позволят это сделать, потому что четко видно где именно проблема и что аффектилось.

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

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

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

Реализация ООП в языках программирования — это уже совсем другое, и возможно к этому и можно придраться, но вот в вашей статье нет НИ СЛОВА про это. Есть только общая критика некоего ООП в вакууме.

Так вот. Основная суть ООП заключается вот в чем:
Есть данные. Есть методы которые напрямую работают с этими данными. Они должны быть в одном «модуле», который в ООП называют классом. Все внешние методы не имеют права обращаться к данными и аффектить их — они обращаются к различным геттерам или сеттерам. Именно поэтому легко и логично соблюдается разграничение ответственности. Легко реализовывается надежная версионность — например стандартное решение вместо правки метода сделать еще один с новым функционалом, сохраняя обратную совместимость.
И не будет такого, что в классе поменяли тип данных с int на float и поломался неизвестный никому компонент, который в этом классе что-то делал. Потому что в ООП он не может что-то делать. Он будет обращаться к геттерам и сеттерам, которые наружу возвращают тот тип данных, который и был ранее.
Такая вот абстракция, которая заменила «модульность».

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

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

Паттерны есть и в ФП, просто более высокоуровневые. Паттерны java-style ООП в ФП это просто функции(по большей части).
Чисто на всякий случай. У вас были проекты, которые разрабатывались командой из более чем 50 разработчиков? А из 100? А проекты на больше чем 100 тысяч строк, а лучше на пару миллионов строк кода?

Зачем спрашивать про большие проекты с кучей разработчиков, если даже небольшие проекты ( < 10 разработчиков за весь жизненный цикл; ~300000строк кода) уже очень сложно поддерживать, с огромными проблемами в производительности и согласованности логики?
Реализация ООП в языках программирования — это уже совсем другое, и возможно к этому и можно придраться, но вот в вашей статье нет НИ СЛОВА про это. Есть только общая критика некоего ООП в вакууме.

В статье критика Java-style ООП(Хотя изначально оно появилось в C++, а может и раньше). И да, этот вариант ООП сейчас самый популярный — в том же C# или PHP, или куче других языков используется именно он.
Так вот. Основная суть ООП заключается вот в чем

Уже не один год работаю с java и до сих пор не понимаю(раньше думал что понимал) в чём суть. Модульное программирование и без ООП существует, полиморфизм подтипов тоже.
Есть данные. Есть методы которые напрямую работают с этими данными. Они должны быть в одном «модуле», который в ООП называют классом. Все внешние методы не имеют права обращаться к данными и аффектить их — они обращаются к различным геттерам или сеттерам.

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

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

Т.е. у Вас private float x; а геттеры и сеттеры int x? Нет, так не бывает, сигнатуру геттеров и сеттеров в этом случае тоже поменяют. Потребуется рефакторинг, который значительно легче проводить в языках вроде hs.
Если взять некое ТЗ и реализовать его хорошими, опытными разработчиками в ООП и функциональном стиле, то я предполагаю, что все варианты в ООП будут либо очень похожи либо идентичны. А в функциональном — будут весьма отличаться.
Очень сомневаюсь, ведь набор хороших практик ООП — это описание того, как не надо делать, а не посыл «как надо».
В итоге появляются всякие мутанты с anemic моделью и hibernate entity торчащими из сервисов.
Обращение к геттерам и сеттерам это и есть прямой доступ к данным, просто с другим синтаксисом

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


Вместо нормального рефакторинга — 10 одинаковых методов с разными версиями?

На самом деле, лучше, конечно, не десять методов, а десять интерфейсов — так ориентироваться удобнее. Но это не суть.


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

Если вам надо сохранить обратную совместимость — а речь именно о таком случае, потому что зачем иначе вся эта боль — то да, именно так.


Т.е. у Вас private float x; а геттеры и сеттеры int x? Нет, так не бывает, сигнатуру геттеров и сеттеров в этом случае тоже поменяют

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

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

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


Геттеры и сеттеры в основном напрямую меняют свойства

Геттеры и сеттеры — это и есть свойства.


потребитель может и не ожидать такого поведения.

Ну это как бы дело потребителя, чего он ожидает или нет? Потребитель и от функции может чего-то "не ожидать".


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

В связности. ООП явным образом группирует данные и операции над ними.


Но геттеры и сеттеры для его сокрытия совершенно не нужны

Конечно, не нужны. Но с ними проще.


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

Можно. Но будет ли это удобно?

Геттеры и сеттеры — это и есть свойства.
Свойства(не в смысле property, как синтаксического сахара для accessor'ов) в данном случае хранятся в полях объекта, а геттеры и сеттеры лишь предоставляют к ним доступ.
Ну это как бы дело потребителя, чего он ожидает или нет? Потребитель и от функции может чего-то «не ожидать».
Это дело создателя API — предусмотреть что от него будет ожидать потребитель. Сделать его понятным. Если я сделаю метод getSomething, который полезет что-то менять в базе — то это будет совсем не очевидно. От функции calcSomething я тоже не ожидаю что она полезет в сеть, и т.д.
Конечно, есть языки где намерения функции можно явно описать в сигнатуре, но в java-style ООП это всё на уровне соглашений.
В связности. ООП явным образом группирует данные и операции над ними.
Их можно группировать в модуле, для этого не нужно ООП.
Можно. Но будет ли это удобно?
Вполне себе удобно.

Свойства(не в смысле property, как синтаксического сахара для accessor'ов) в данном случае хранятся в полях объекта, а геттеры и сеттеры лишь предоставляют к ним доступ.

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


Это дело создателя API — предусмотреть что от него будет ожидать потребитель. Сделать его понятным.

Ну так оно и есть понятно и описано. Но это никак не мешает пользователю иметь свои собственные ожидания.


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


От функции calcSomething я тоже не ожидаю что она полезет в сеть, и т.д.

Ну вот смотрите. Вы не ожидаете. А если я вызываю операцию (не функцию, это важно) "calcCurrentETA", я вполне ожидаю, что она может полезть куда-нибудь за состоянием трафика, доступностью машин и так далее.


Их можно группировать в модуле, для этого не нужно ООП.

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


Вполне себе удобно.

Ну вот а кому-то удобно делать то же самое в ООП.

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

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

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


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

Можно. Но это как раз нарушение сокрытия — вы больше не можете изменить поведение свойства прозрачно для потребителей.


Всё описывать может и нельзя, но в данном случае речь о том, что информации недостаточно(для понимания пользователем).

Ну так описывайте так, чтобы было достаточно, в чем проблема-то?


В ФП у меня есть возможность явно указать на возможное поведение функции

Угу. То есть в первой версии функции вы укажете, что она считает внутри, во второй — что она лезет в БД, а в третьей — что она лезет в БД и внешний сервис. И каждый раз вам придется править вызывающий код, правильно?


Понимаете ли, один из способов борьбы со сложностью — это не знать, что делает вызываемый код (если это не важно). Вы предлагаете от этого отказаться.


ООП это просто другое название для модульного программирования?

Нет.

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

Почему потребителю это не важно? Ещё как важно.
Можно. Но это как раз нарушение сокрытия — вы больше не можете изменить поведение свойства прозрачно для потребителей
Если тут и нарушение чего-либо, то точно не принципа сокрытия. Ведь accessor'ы точно так же дают доступ потребителю, ничего фактически они не скрывают. К тому же принципов программирования столько, что нарушать их — это норма. Accessor'ы, к примеру, нарушают принципы KISS и YAGNI.(А огромное количество boilerplate кода в Java мешают навигации по коду)
Ну так описывайте так, чтобы было достаточно, в чем проблема-то?
Как мне описать геттер таким образом, что-бы потребитель был уверен, что ничего сверхъестественного не произойдёт?(вернётся значения поля)
Угу. То есть в первой версии функции вы укажете, что она считает внутри, во второй — что она лезет в БД, а в третьей — что она лезет в БД и внешний сервис. И каждый раз вам придется править вызывающий код, правильно?
Да, и это правильно. Например, я поменял функцию calcSomething так, что она теперь лезет в базу. Всё отлично, до тех пор пока код, который этого не знал, не решит обратиться к методу calcSomething из другого потока, в котором не установлен контекст для работы с БД. И узнаю я об этом только в рантайме.
Понимаете ли, один из способов борьбы со сложностью — это не знать, что делает вызываемый код (если это не важно). Вы предлагаете от этого отказаться.
Предлагаю отказаться тогда, когда это важно.
Почему потребителю это не важно?

Потому что сокрытие ненужной реализации. Зачем потребителю это знание?


Ведь accessor'ы точно так же дают доступ потребителю, ничего фактически они не скрывают.

Почему не скрывая-то? Accessor — это метод, если он не скрывает, то у вас вообще невозможно никакое сокрытие.


Как мне описать геттер таким образом, что-бы потребитель был уверен, что ничего сверхъестественного не произойдёт?

Никак. Потому что это противоречит задаче геттера.


Но если вам так хочется — идете и пишете в документации.


Да, и это правильно

А что делать, если есть три разных реализации, которые выбираются на этапе конфигурации приложения?


не решит обратиться к методу calcSomething из другого потока, в котором не установлен контекст для работы с БД

А не надо устанавливать контекст в потоке.


Предлагаю отказаться тогда, когда это важно.

И как это сделать, если по функции всегда должно быть понятно, какие у нее есть побочные эффекты?

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

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

Нет, не объяснили.


Сокрытие это ложная цель.

Что же в ней ложного? Как еще вы предлагаете управлять сложностью?


Предусмотреть ограничение, включающее эти возможные варианты.

То есть теперь мы будем видеть ограничение, которое говорит "у функции может быть любой побочный эффект", но не знаем, какой конкретно побочный эффект она имеет?


Почему?

Потому что ambient context — очень спорный паттерн.


А если это hibernate entity — не подскажите, как с ним из другого потока работать(даже если контекст установлен)?

Ничего не знаю про hibernate entities.


в ООП — не знаю.

А я сейчас спрашиваю не про ООП, а про предлагаемую вами парадигму.

Нет, не объяснили.
Тогда повторю: АПИ декларирует что при доступе к свойству никаких побочных эффектов не будет.
Что же в ней ложного? Как еще вы предлагаете управлять сложностью?
Как и обычно — разделить данные и функции, а не смешивать их.
На самом деле при использовании анемичной модели именно так и происходит, и accessor'ы там смотрятся как нечто совершенно не нужное.
То есть теперь мы будем видеть ограничение, которое говорит «у функции может быть любой побочный эффект», но не знаем, какой конкретно побочный эффект она имеет?
можно «любой», можно просто один из вариантов. Зависит от слоя и уровня АПИ.
Потому что ambient context — очень спорный паттерн.
Но он, тем не менее, используется. К тому же threadlocal контекст не единственная из возможных проблем. Даже если контекст удалось установить, он может находиться в нежелательном состоянии(Например, не запущена транзакция, или требуется авторизация).
А я сейчас спрашиваю не про ООП, а про предлагаемую вами парадигму.

getSomething :: (MonadDatabase m, MonadNetwork m) => m Something

или
getContractById :: MonadDocumentFlow m => ContractId -> m Contract

а уже в ограничениях реализации монады MonadDocumentFlow указывается, какой контекст ей требуется. И без контекста БД её запустить не получится.

Тогда повторю: АПИ декларирует что при доступе к свойству никаких побочных эффектов не будет.

Где декларирует?


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

Как вам это поможет бороться со сложностью реализации самих функций?


можно «любой», можно просто один из вариантов.

"Один из вариантов" — нельзя, мы не знаем, какой будет в рантайме.


Но он, тем не менее, используется.

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


а уже в ограничениях реализации монады MonadDocumentFlow указывается, какой контекст ей требуется

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


(и да, вы только что продемонстировали, что сокрытие — не ложная цель)

Где декларирует?
?
Как вам это поможет бороться со сложностью реализации самих функций?
как минимум будет известно в каких местах точно не произойдёт ошибки.
«Один из вариантов» — нельзя, мы не знаем, какой будет в рантайме.
можно запросить сразу несколько в конкретной реализации монады. Сделать прокси-монаду, которая будет выбирать конкретную реализацию(если требуется переключение в рантайме).
Ну так много что используется, что не надо бы. Это же не повод на это ссылаться как на ошибку парадигмы.
повод, если это реальный пример. К тому же я и другие варианты предложил.
А теперь пользователь говорит: нет, я хочу знать, какие побочные эффекты потенциально возможны, и к какому контексту я могу или не могу обращаться.
он это и так знает, все ограничения описаны в функции запуска конкретной реализации монады.
runDocumentFlowT :: (MonadDatabase m, MonadNetwork m) => DocumentFlowT m a -> m a
?

Вы приводите "АПИ декларирует что при доступе к свойству никаких побочных эффектов не будет." как ответ на мой вопрос "Зачем потребителю это знание?"


Я не понимаю связи.


как минимум будет известно в каких местах точно не произойдёт ошибки.

Это, простите, как? У вас разработчики не ошибаются?


Или вы имеете в виду "не произойдет ошибки конкретного типа"?


Сделать прокси-монаду, которая будет выбирать конкретную реализацию(если требуется переключение в рантайме).

Ну так в этот момент вы вернулись к ситуации, когда вызывающий код не знает, с чем он работает.


повод, если это реальный пример.

Реальный пример чего? Ambient context — это не ОО-специфичный паттерн.


он это и так знает, все ограничения описаны в функции запуска конкретной реализации монады.

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


Так и ходим по кругу, да.


А знаете, что самое грустное? Самое грустное то, что это не про ООП. Это про явные и неявные побочные эффекты.

Я не понимаю связи.
значит я отвечал не на тот вопрос. Зачем потребителю это знание? Затем, что потребитель — программист, и он использует функционал, описываемый АПИ. В сказочном мире он ни о чём знать не должен — ну возвращает метод Something, какая разница откуда он это берёт? В реальности это имеет значение. Не всегда программа может находиться в нужном состоянии, может быть соединение с базой не установлено, или из определённого места не предполагается доступ к файлам и иные побочные эффекты. В случае, когда в АПИ описан доступ через поле — программист знает, что никаких дополнительных условий не требуется.
Или вы имеете в виду «не произойдет ошибки конкретного типа»?
runtime error скорее.
Реальный пример чего? Ambient context — это не ОО-специфичный паттерн.
Реальный пример того, как состояние контекста приводит к ошибкам при вызове метода.
Тогда вы не решили задачу сокрытия, которую, как вы говорили, можно решить.
Во первых я говорил что это ложная цель. Во вторых — почему не решил? Вполне себе. Вы пишите функцию
calcSomething :: MonadDocumentFlowT m => ContractId -> m Int
calcSomething cId = do
  c <- getContractById cId
  return (field1 c + field2 c)

которая что-то там считает по контракту. Ей всё равно, откуда берётся контракт. Но запустить это функцию там, где не установлен контекст для работы с БД(или что там требуется для запуска реализации) — не получится.
А знаете, что самое грустное? Самое грустное то, что это не про ООП. Это про явные и неявные побочные эффекты.
Самое грустное что ООП не решает реальные проблемы, и я говорю именно об этом.
Зачем потребителю это знание? Затем, что потребитель — программист, и он использует функционал, описываемый АПИ.

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


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

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


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

Вот только это не ОО-проблема.


Ей всё равно, откуда берётся контракт. Но запустить это функцию там, где не установлен контекст для работы с БД(или что там требуется для запуска реализации) — не получится.

… и в этот момент, она не знает, какие побочные эффекты произойдут при ее выполнении.


Самое грустное что ООП не решает реальные проблемы

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

Пусть код прочитает, если ему интересны детали. И, как программист, понимает, что код — это не контракт.
А разработчик этого АПИ знает, что в этот момент он подписался под слишком строгим контрактом, и постарается этого избежать.
Какой хитрый разработчик. Пользователь прочитает код, а разработчик в следующем релизе ему что-то сломает.
Только, как инженер могу сказать что ни к какому качеству и надёжности это отношение не имеет. И программисты должны избегать такого подхода, а не считать его чем-то хорошим.
Вот только это не ОО-проблема
Это именно ОО-проблема, ведь ООП не предлагает механизма для решения подобных задач. То, что там внутри threadlocal контекст — лишь деталь реализации, верно?
… и в этот момент, она не знает, какие побочные эффекты произойдут при ее выполнении.
это вопрос или что? Да, не знает. Зато программист знает что некоторые эффекты есть, а какие конкретно он знает когда запускает реализацию монады.
Компилятор просто не позволит ему использовать БД там, где для неё не установлен контекст.
Да нет, ООП решает реальные проблемы — в частности, реальную проблему управления сложностью. Если бы оно их не решало, им бы не пользовались. Да, оно решает их не идеально, с этим никто не спорит. Ну так языки программирования и не стоят на месте.
По моему ООП чаще создаёт больше проблем чем решает. Это, кстати, объясняет популярность совсем не ОО анемичной модели.
Какой хитрый разработчик. Пользователь прочитает код, а разработчик в следующем релизе ему что-то сломает.

Если поведение не было явно обещано — оно не сломано. Не надо закладываться на что-то за пределами контракта.


Только, как инженер могу сказать что ни к какому качеству и надёжности это отношение не имеет.

Ну да, потому что это имеет отношение к простоте изменения. И эти вещи друг с другом конфликтуют, да.


Это именно ОО-проблема, ведь ООП не предлагает механизма для решения подобных задач.

Предлагает: передавайте контекст явно.


То, что там внутри threadlocal контекст — лишь деталь реализации, верно?

Это не-ОО-специфичная деталь реализации. Контексты существовали (и широко использовались) в "чистом" процедурном программировании. ОО, если уж на то пошло, пытается их уменьшить.


Зато программист знает что некоторые эффекты есть, а какие конкретно он знает когда запускает реализацию монады.

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


Компилятор просто не позволит ему использовать БД там, где для неё не установлен контекст.

Вы мне еще скажите, что компилятор не позволит не использовать БД, если она передана.


По моему ООП чаще создаёт больше проблем чем решает.

Ну, по-вашему — так. По-моему — не так. Никакого способа формально это проверить нет.

Если поведение не было явно обещано — оно не сломано. Не надо закладываться на что-то за пределами контракта.
Если ни на что не опираться, то так не сложно и упасть. Без каких либо гарантий — это плохой код.
Предлагает: передавайте контекст явно
Something getSomething(). Куда передавать контекст?
Это не-ОО-специфичная деталь реализации. Контексты существовали (и широко использовались) в «чистом» процедурном программировании. ОО, если уж на то пошло, пытается их уменьшить.
Контекст существует в любой программе, не только процедурной. Тут вопрос в управлении контекстом.
В любом случае пример был реальный, с настоящим ОО кодом — реализацией JPA, так что тут глупо спорить.
Это два разных программиста — тот, который написал код внутри «плагина», и тот, который этот код вызвал. Первый не знает, как будет устроена монада, на которую он опирается, а второй не знает, какими конкретно эффектами монады воспользовался первый.
да, программист, написавший calcSomething знает только интерфейс монады. В этом месте он абстрагируется от вызова конкретного кода.
Но он знает что ожидать от вызова getContractById.
В случае же с геттером, который начал лезть в базу и упал с exception — никто этого не ожидает, этого нет в АПИ.
Вы мне еще скажите, что компилятор не позволит не использовать БД, если она передана.
?
Без каких либо гарантий — это плохой код

Ну почему же сразу "без каких-либо". Определенные гарантии у него есть.


Something getSomething(). Куда передавать контекст?

getSomething(context), если вы хотите явное, или Something(context).getSomething(), если вы хотите внешнее.


В любом случае пример был реальный, с настоящим ОО кодом — реализацией JPA, так что тут глупо спорить.

Неа, это не "настоящий ОО-код". Это код, написанный на ОО-языке.


Но он знает что ожидать от вызова getContractById.

И чего же?


?

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

Ну почему же сразу «без каких-либо». Определенные гарантии у него есть.
Минимум гарантий.
getSomething(context), если вы хотите явное, или Something(context).getSomething(), если вы хотите внешнее.

отлично, как мне понять, что getSomething не надо обвешивать try/catch и опасаться за производительность?
Неа, это не «настоящий ОО-код». Это код, написанный на ОО-языке.
Детали реализации, опять же. В том плане что «потребителю не важно откуда берётся значение» — вполне себе ОО код.
И чего же?
Что может произойти runtime ошибка, например.
Вот у вас есть функция, которая, по сигнатуре, возвращает результат вычисления, зависящий от БД. Может ли она, на самом деле, от БД не зависеть?
может.
Минимум гарантий.

Не "минимум", а сколько вам даст разработчик АПИ.


отлично, как мне понять, что getSomething не надо обвешивать try/catch и опасаться за производительность?

А более-менее никак. Детали будут зависеть от языка и среды выполнения.


В том плане что «потребителю не важно откуда берётся значение» — вполне себе ОО код.

Неа, это не ОО-код. Это код с абстракцией. В функционально-ориентированных языках та же самая фигня: мы вызываем order |> getCustomer |> context.bind (потому что getCustomer возвращает монаду, который нужен контекст), и мы больше не знаем, откуда что взялось.


Что может произойти runtime ошибка, например.

Каким образом, если этот getContractById зависит от БД, а БД может быть недоступна — и мы получим ошибку недоступности БД?


может.

Ну вот видите. Это значит, что вы глядите на сигнатуру, но вы не знаете, от чего зависит поведение. Вы знаете максимум, но не минимум.

Не «минимум», а сколько вам даст разработчик АПИ.
А более-менее никак. Детали будут зависеть от языка и среды выполнения.
Хорошо
Неа, это не ОО-код. Это код с абстракцией. В функционально-ориентированных языках та же самая фигня: мы вызываем order |> getCustomer |> context.bind (потому что getCustomer возвращает монаду, который нужен контекст), и мы больше не знаем, откуда что взялось.
Вам виднее что ОО, а что нет, так что соглашусь. Таким образом мы разобрались с тем что геттеры это норм.
Про монаду не очень понял.
Каким образом, если этот getContractById зависит от БД, а БД может быть недоступна — и мы получим ошибку недоступности БД?
АПИ декларирует недоступность БД, например, как один из вариантов ошибки. Либо более общий тип ошибки, если логика этого требует.
Ну вот видите. Это значит, что вы глядите на сигнатуру, но вы не знаете, от чего зависит поведение. Вы знаете максимум, но не минимум.
Да, Вы правы.
Таким образом мы разобрались с тем что геттеры это норм.

А я вроде всегда говорил, что геттеры — это нормально.


Про монаду не очень понял.

Я не помню точно функциональную терминологию, но смысл в том, что вы все равно куда-то прячете операцию с контекстом, и конкретный код, который достает что-то, что зависит от этого контекста через три посредника, об этом не знает (или, если знает, то не знает, как именно оно внутри работает).


АПИ декларирует недоступность БД, например, как один из вариантов ошибки.

… и это ошибка времени выполнения. Так что вы от нее никуда не избавились.

А я вроде всегда говорил, что геттеры — это нормально.
так и есть
Я не помню точно функциональную терминологию, но смысл в том, что вы все равно куда-то прячете операцию с контекстом, и конкретный код, который достает что-то, что зависит от этого контекста через три посредника, об этом не знает (или, если знает, то не знает, как именно оно внутри работает).
разница в том, что контекст работы функции описан в её сигнатуре.
… и это ошибка времени выполнения. Так что вы от нее никуда не избавились.
действительно
разница в том, что контекст работы функции описан в её сигнатуре.

Максимально нужный. Вы не знаете, что в реальности она с ним делает.

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


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

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

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


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


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

дело самого объекта, его пользователей это волновать не должн
В какой-то теории — может быть. На практике — ещё как волнует, в этом и вопрос.
Про базу: в нормально спроектированном приложении, объект, которому нужна база, получает контекст или при конструировании, или параметром вызова метода
В hibernate сущность привязана к threadlocal контексту, и достать её оттуда — нельзя. Насколько hibernate нормально спроектирован — судить не могу.
(И да, я попробовал инъекцию в сущность после нашего прошлого обсуждения — с hibernate это довольно геморно, приходится даже менять флаги запуска в jvm; там суть в том что меняется поведение java и некий агент в classloader'е получает возможность выполнять код после конструирования объекта)
ООП можно рассматривать как вариацию модульного программирования
по мне так это действительно просто модульное процедурное программирование, с прикрученным сбоку механизмом полиморфизма подтипов и дутой теорией.
Потому что ООП — это принцип группировки данных и операций над ними
Точнее, ООП — это способ удобно работать с многими состояниями системы. То есть локализация стейтов. А как там вокруг этих стейтов методы с полями группируются — дело десятое. Обычно это сделано коряво.

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

Структуры данных всегда_готовы к записи (например). Тогда как у объекта есть характер: он не запишет -1 стул в БД и т.д. Функции «не видят» допустимых границ. А если сделать, чтобы видели — то это уже и есть ООП.

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

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

Публикации

Истории