Pull to refresh

Comments 118

Для применения статических методов нет ни единого оправдания ни в одной ситуации. Забота о производительности не считается
Вот как вычисление большего из двух чисел должно выглядеть в ООП. Number x = new Max(5, 9);
Т.е. вместо вызова быстрого статического метода для нахождения максимума автор предлагает создавать новый объект? Я ничего не упускаю?

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

И еще одну, что бы собирать весь создаваемый мусор.


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

Почему бы не использовать всю палитру взможностей языка а не ограничивать себя лишь объектами? Не говоря уж о том, что большинство современных языков сознательно делают мультипарадигменными, а вы предлагаете сделать шаг назад и ограничиваться только объектно ориентированной парадигмой.
Неокрепшие умы теперь буду аргументировать адовые подходы тем, что «про это же в умной книжке написано!».
UFO just landed and posted this here
Автору надо писать код на Smalltalk либо на Ruby там ООП реализовано полноценно, но лучше конечно Smalltalk ибо это отец ))
Автор на Ruby в том числе очень много пишет. Посмотрите его github.
Просто довольно странно жаловаться на реализацию ООП в языке который по факту не когда не имела полноценную поддержку ООП. Это тоже самое жаловаться на самокат потому что у него нет двигателя

Это какой язык никогда не имел полноценной поддержки ООП?

А вы про какой ООП говорите? общепринятый или тот который от Алана Кея? Все же есть разница. Разница на уровне что ООП Кея было все же интереснее чем ООП которое было в Simula (да даже ооп в симуле было интереснее чем ООП в java), а поскольку из выживших и используемых языков из тех что подходят под определения Кея остался только Erlang… то лучше просто забить и смотреть в сторону всяких там F# и подобных (что-нибудь с сильной системой типов и упором на функциональную абстракцию а не на структурное программирование).
А вы про какой ООП говорите?

Ну в основном мне, конечно, интересно какой ООП имел в виду комментатор, которому я отвечал.

«общепринятый или тот который от Алана Кея» — Хмм, ну походу тут второй вариант ответа я имел ввиду исходя из моего комментария где я советовал Автору статьи писать код на Smalltalk and Ruby.
Smalltalk досих пор жив, вам не кто не запрещает его использовать, даже веб фреймы есть ))
+ Ruby хоть и не на 100% канон, но очень близок.
У моего самоката двигатель все же есть.
Много лет использую статические методы в средних и больших проектах. Никогда не выхватывал проблем из-за этого. Видимо автор живет в каком-то своем, особом мире. Да и фанатичностью от текста сильно попахивает.
Абсолютно все пишут в процедурном стиле. Этот же стиль пропагандируется создателями популярнейших фреймворков, типичный пример Spring, где у тебя есть контроллер, который вызывает внутри себя сервис который внутри допустим создает объект с геттерами и сеттерами и сохраняет его используя репозиторий.
Весь этот код от начала до конца процедурный, ООП там и не пахнет.

Конечно если все так пишут то они и не замечают никаких проблем, потому что просто не знают что может быть по-другому. А вот код Егора позволяет действительно собирать программу из маленьких кусочков каждый из которых делает исключительно то, что нужно (и обладает состоянием!).

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

Абсолютно все пишут в процедурном стиле.

Потому, что он воспринимается более декларативно. А человек — такое существо, мышление которого заточено под простые, линейные алгоритмы.
Сначала линейный (процедурный)
Открыть машину              [openCarDoor()]
Сесть на место водителя     [takeADriversSeat()]
Завести машину              [startEngine()]

Теперь ООП
"Попросить машину" открыть дверь                     [car.openDoor()]
"Попросить машину" разместить меня на месте водителя [car.setDriver(myself)]
"Попросить машину" завестись                         [car.startEngine()]

Первое гораздо ближе к тому, как думает человек.

То же самое, кстати, касается и асинхронности. Посмотрите как создатели Kotlin'а подошли к внедрению корутин и чем аргументируют — ровно тем, же, что линейная структура кода (который под капотам ни черта не линейно ессно) понимается и воспринимается на порядки проще, чем callback-hell или нагромождение async-await'ов
Мне кажется на ООП этот код мог выглядеть немного подругому, например:
new Машина_С_Пассажирами(машина, [коля, вася, петя])


А где-то в недрах класса «Машина_С_Пассажирами» должна быть проверка на открыть дверей в объекте «машина» (ну или если абстрактно — вообще на доступность посадки).
вы сейчас привели в пример оркестрацию, без условий и какой-либо логики. тупо последовательность действий. Этот код будет выглядеть идентично вне зависимости от выбранного стиля.

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

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


зачем вы тут искали сложную логику.

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


Я предположу что у того как выглядит большая часть кода причина несколько иная нежели "нам так думать естественнее". Когда люди учатся они обычно смотрят на то, как это делают другие. Так или иначе формируется представление о том как надо делать дела, за счет практики это представление формируется в привычку, ну и опять же — никто не думает что делает. Так растет случайная сложность, связанность и т.д.


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

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

Как минимум, "Открыть Машину" — это не ответственность класса "Человек". То, что вы предлагаете — это god object, который умеет делать всё и со всем. Одна из самых опасных и плохих практик в ООП.

Нет, это не god object. Этот человек не умеет всего и не знает всего. Это, как и реальном мире, просто один конкретный человек который может открыть (а может и не открыть) конкретную машину.

"открыта" и "закрыта" — это состояние машины. Каким образом вы предполагаете изменять состояние объекта "машина" из объекта "человек"?

А каким образом меняется это состояние в первом примере?

А в первом примере это собственное состояние и как его меняет объект владелец — не так уж и важно.

И тут можно зайти так же, но со стороны человека. Внутри ОткрытьМашину() он сам решает, что применит (сигналка, ключ, фомка, гидравлические клещи, прочее), но снаружи это не так уж и важно.

Состояние машины то как менять будем?

Внутри машины в зависимости от результатов воздействия через доступные интерфейсы.

Т.е. мы снова возвращаемся к тому, с чего начали "Машина.ОткрытьДверь()", только при этом обременив класс Человек не свойственной ему ответственностью, либо делаем сеттер, наплевав на инкапсуляцию.

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

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


В данном случае, практически нет разницы, у Человека или Машины есть метод открытьМашину(), т.к. непонятны сценарии использования (как будет удобнее вызывать этот метод). Как только сценарии будут ясны, ответ на вопрос станет более очевидным.

Можете привести пример "православного ООП" и на примере же показать почему Спринг это не ООП?

Главная фича ООП это полиморфизм, который в спринге считай не используется. Там как правило контроллер не реализует интерфейсы. В крайнем случае есть тестовая реализация и основная.


Кроме того контроллеры и всё остальное как правило не имеют состояния и ничего не инкапсулируют.


Православное ООП это интерфейс List в джаве, под который можно написать свою реализацию и передать её везде, где нужен List.

Главная фича ООП это полиморфизм, который в спринге считай не используется. Там как правило контроллер не реализует интерфейсы. В крайнем случае есть тестовая реализация и основная.

Простите, а зачем вам полиморфизм в контроллерах? Как выглядят контроллеры в православных ООП языках и в чем их фишка?


Кроме того контроллеры и всё остальное как правило не имеют состояния и ничего не инкапсулируют.

Ну, такая уж идеология фреймворка — данные (стейт) храните в базе. А вообще — делайте как хотите: As a rule of thumb, you should use the prototype scope for all beans that are stateful, while the singleton scope should be used for stateless beans


Православное ООП это интерфейс List в джаве, под который можно написать свою реализацию и передать её везде, где нужен List.

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

Простите, а зачем вам полиморфизм в контроллерах?

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


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

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


Вы можете написать интерфейс и использовать его везде

Можем, но в этом как правило нет особенной необходимости и особенного смысла.

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

Ну так веб-сервер (а мы говорим о спринге в контексте веб серверов в данный момент насколько я понимаю) это приложение, которое обрабатывает запросы. Возможно тогда дело не в том, что Спринг не ООП (потому что это не так), а в том что обсуждаемый нами use-case не требует применения ООП в типичных сценариях?

Возможно тогда дело не в том, что Спринг не ООП

"Спринг не ООП" звучит не очень хорошо. Лучше сказать, что при использовании спринга не нужно применять ООП. Можно применять, но необходимости нет, API спринга ООП почти не утилизирует.


а в том что обсуждаемый нами use-case не требует применения ООП в типичных сценариях?

А в каких сценариях при использовании спринга применяется ООП?

Учитывая обложку книги… А это точно не глобальный троллинг? :D

А вообще это просто кладезь хвостатыхкрылатых фраз.
Мы мыслим как функция, а не как компьютер.
Я просто инстанцирую объект. Это очень похоже на def в Lisp… В этом смысле ООП очень похоже на функциональное программирование.
Есть два подхода — декларативный и процедурный
Статические методы напоминают раковую болезнь объектно-ориентированного ПО
Просто инкапсулируйте пользователя во все объекты, в которых он может пригодиться.
Не нужны if, for, switch и while. Нам нужны классы If, For, Switch и While. Чувствуете разницу?
В мемориз, однозначно!
> Есть два подхода — декларативный и процедурный
может автор или переводчик ошибся?
так как почти везде используется «императивный».

А так можно было бы и почитать книженцию ради альтернативного мнения, иногда это подталкивает к интересным мыслям))
А книга разве переводная?
В любом случае, «процедурный» используется в тексте очень часто. Вот еще цитатки:
В коде совсем не должно быть процедурных операторов вроде if, for, switch и while.
В Java, как и в Ruby и во многих других недо-ООП-языках, глобальные переменные запрещены. Почему? Потому что они не имеют никакого отношения к ООП. Это чисто процедурная возможность.
Оператор if предоставляется языком Java и используется нами в процедурном ключе, оператор за оператором.
Не похоже на ошибку, если честно. Скорее на кашу в голове, где прочно обосновалась идея «императивный == процедурный».
ну изначально он эту книгу на английском написал… а там кто знает, кто знает…
> В Java, как и в Ruby и во многих других недо-ООП-языках, глобальные переменные запрещены. Почему? Потому что они не имеют никакого отношения к ООП. Это чисто процедурная возможность.

вот с этим я наверное соглашусь.
все остальное звучит действительно странновато…
Ну в общем чистое ООП)))
Вы правда считаете, что глобальные переменные — это фишка именно процедурного программирования? И что в Java/Ruby они запрещены?
Я более чем уверен что они зло, запрещены они или нет это уже другой разговор, и да в процедурных языках они использовались хранения глобального состояния, в ООП этого можно избежать.
процедурных языках они использовались хранения глобального состояния, в ООП этого можно избежать.
Ну так никто не мешает сделать тоже самое и при процедурном программировании. Создаете структурку, храните данные в ней, при необходимости проталкиваете ее в подпрограммы через ссылку на стеке (локальную переменную).

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


Из кники «Чистая архитектура. Искусство разработки программного обеспечения.»
Структурное программирование

Дейкстра показал, что безудержное использование переходов (инструкций
goto ) вредно для структуры программы. Как будет описано в последующих главах, он предложил заменить переходы более понятными конструкциями >if >/ then / else и do / while / until.
Подводя итог, можно сказать, что:
Структурное программирование накладывает ограничение на прямую передачу управления.


то есть глобальную память никто не отменял…

с точки зрения программиста имеются программа и память
Так а глобальные переменные то где? :)
Стек ведь тоже хранится в памяти (локальные переменные).
И состояние объектов в ООП.

Структурное программирование накладывает ограничение на прямую передачу управления.
Все так. Но я в упор не понимаю, как это относится к теме глобальных переменных.
А там и не указывается что именно стек… Просто память, она может быть и глобальной.
Структурное программирование добавляет ограничения в виде конструкций «if/ then / else и do / while / until.» и не какого «goto»
ООП в свою очередь добавляет Инкапсуляцию.
А ФП вообще иммутабельность.
Как раз такой подход Стивен Прата в книге «Язык программирования C. Лекции и упражнения» приводит как пример написания программ на С в стиле ООП…
Не назовете номер главы? В идиале еще и страницы.
Интересно глянуть на пример ;)

А еще ООП подразумевает наследование/полиморфизм. Которые не нужны в упомянутом выше подходе (пробрасывать состояние через ссылки на стеке).
Не могу вспомнить, я читал ее уж слишком давно…
И на руках ее нет, а так где-то к концу книге.
но могу из этой…
Мартин Р. " Чистая архитектура. Искусство разработки программного обеспечения"

Объектно-ориентированное
программирование
Второй парадигмой, получившей широкое распространение, стала пара-
дигма, в действительности появившаяся двумя годами ранее, в 1966-м,
и предложенная Оле-Йоханом Далем и Кристеном Нюгором. Эти два программиста заметили, что в языке ALGOL есть возможность переместить кадр стека вызова функции в динамическую память (кучу), благодаря чему локальные переменные, объявленные внутри функции, могут сохраняться после выхода из нее. В результате функция превращалась в конструктор класса, локальные переменные — в переменные экземпляра, а вложенные функции — в методы. Это привело к открытию полиморфизма через строгое использование указателей на функции.

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


Там на самом деле много еще написано, в других книгах… но мне уже лень))
Не могу вспомнить, я читал ее уж слишком давно…
Яснопонятно.

но могу из этой…
Спасибо за цитату, но как она соотносится с темой нашего разговора? Да там явно написано про локальные переменные, мол придумали как их переместить на кучу, сделав некое замыкание.
благодаря чему локальные переменные, объявленные внутри функции, могут сохраняться после выхода из нее.
пойдем другим путем))
где в определениях процедурного(и структурного) программирования говорится
> Создаете структурку, храните данные в ней, при необходимости проталкиваете ее в подпрограммы через ссылку на стеке (локальную переменную).

там говорится про память… то есть возможность использовать глобальные переменные

В ООП инкапусляция говорит что переменные должны быть в объекте и доступ к ним должен осуществляться через интерфейс объекта(то есть глобальные переменные сразу отсекаются)

таким образом глобальные переменные явно или неявно относятся к процедурному программированию
там говорится про память… то есть возможность использовать глобальные переменные
Память != глобальные переменные. Вы упорно продолжаете фантазировать.

В ООП инкапусляция говорит...
Только вот она ничего не говорит о том, что не может быть синглтонов.
Память != глобальные переменные, да, представьте себе.
Но это не означает что программа не может использовать глобальные переменные, которые хранятся так же в памяти и в определениях не написано что надо обязательно использовать стек.
Так что вы доказать то пытаетесь?
Что в процедурном программировании можно использовать глобальные переменные? Можно.
При ООП тоже можно. Дальше то что?
я не пытаюсь чего либо доказать.

> Что в процедурном программировании можно использовать глобальные переменные? Можно.
При ООП тоже можно. Дальше то что?

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

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

Да и надоела мне эта беседа)
Ох как грубо))
Сразу минус))

Вы не приводите аргументы, а задаете вопросы, это похоже больше на экзамен чем на беседу…
намекну… это противоречит Инкапсуляции...)))
Нисколько не грубо :D
Вы просто «слились», достаточно неумело. Минус именно за неумелость.

А еще за попытку аргументации к авторитету (спасибо за неуместные ссылки на книги, ага).

>Вы просто «слились», достаточно неумело.
предлагаете по 20 раз вам писать одно и тоже?

уместные ссылки на книги… они авторитетнее моего мнения и вашего.

Ладно… перейдем к самому интересному.
где в ООП есть понятие переменная?
есть объекты у которых есть методы и свойства.
доступ к свойствам должен осуществляться через методы объекта. И не важно синглтон это или нет. (полиморфизм, наследование не рассматриваем, к теме о глобальных переменных это не относиться)
И не важно синглтон это или нет.
Как раз важно. Даже в этой статье упоминается что синглтон по сути эквивалентен глобальной переменной. Скажем, у вас есть синглтон 'document', а у него метод 'getRootNode'.

Вообще предлагаю расширить вашу логику:
Где в ООП есть понятие цикла? Это чисто процедурная возможность.
Где в ООП есть понятие подпрограммы? Это чисто процедурная возможность.
Где в ООП есть понятие числа? Это чисто процедурная возможность.

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

опять много вопросов…
Где в ООП есть понятие числа? — объект?

Где в ООП есть понятие цикла? Это чисто процедурная возможность.
Где в ООП есть понятие подпрограммы? Это чисто процедурная возможность.
разве?

А мне вот кажется в ООП убрали понятие перемменая… и перешли на абстракции…

>Как раз важно. Даже в этой статье упоминается что синглтон по сути эквивалентен глобальной переменной. Скажем, у вас есть синглтон 'document', а у него метод 'getRootNode'.

и как это противоречит «есть объекты у которых есть методы и свойства.
доступ к свойствам должен осуществляться через методы объекта.»?

>Даже в этой статье упоминается что синглтон по сути эквивалентен глобальной переменной.
Вы же сами говорите что статья бредовая)
>Все это время я пытался подвести, что как раз возможность дать объектам-синглтонам глобальный идентификатор (т.е. по сути положить их в глобальную переменную) не противоречит идеи ООП. Т.е. это ортогональные понятия.

нету тут понятия глобальной переменной.
так как состоянием синглтоноа управляет сам синглтон.

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

к глобальной переменой может обратиться любая часть программы и изменить ее.
А мне вот кажется в ООП убрали понятие перемменая… и перешли на абстракции…
Спасибо за еще одну сакральную фразу!

По поводу сиглтонов и глобальных переменных. Конечно можно настаивать что это «аттрибуты», а не переменные и т.п. Но обычно придерживаются более свободной терминологии. Так гляньте в гайд к Squeak, там упоминается 'global scope' (хотя по факту все как вы написали). Или в Javascript мы создаем глобальную переменную, а под капотом создается атрибут у глобального объекта (window).

к глобальной переменой может обратиться любая часть программы и изменить ее.
Ну что за бред. Модульность, области видимости, различные скоупы, АТД и все такое?

Еще раз подчеркну, что при процедурном программировании вовсе не обязательно иметь глобальные переменные (не более, чем при ООП). Не путайте его со структурным.
нет переменных в ООП…
User user = new User();
«Создан объект user» — ООП
«переменой user присвоена ссылка из кучи...» — не ООП
сама терминология разная.
В JAVA без костылей глобальную переменную нельзя создать, можно только создать класс в котором будут открытые статические свойства класса, но это уже костыль.
В Ruby почти все запихнули в объект, как пример
"-1234.abs"…
Не надо путать парадигму с реализацией
необязательно != невозможно, парадигма этого не запрещает.

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

Вот конкретно с этим пунктом не всё так очевидно. Автор, конечно, отжигает по-полной, но здравые идеи иногда попадаются и среди такого. Неявные зависимости от глобальных переменных/синглтонов и явные зависимости от конкретных библиотек/статических методов действительно может иметь смысл передавать объекту при создании явно. Конечно, речь не о том, чтобы передавать каждому объекту свою реализацию функций вроде max/between, но вот сессии/пользователи, доступ к БД, логгер, контекст текущего запроса — эти значения нужны почти везде, и передавать их всюду явно ручками лениво, но это заметно добавляет коду явности и тестируемости, так что резон в этом есть: A theory of modern Go.

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

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

Если первый объект не лезет в потраха второго, а использует только продекларированные втором объектом интерфейсы, то почему нет? Всю реализацию второй от первого в себе скрывает.
Я просто инстанцирую объект.
Просто инкапсулируйте пользователя во все объекты, в которых он может пригодиться.
Не нужны if, for, switch и while. Нам нужны классы If, For, Switch и While.

Напомнило телемагазины: вы просто прикрепляете тренажер, все остальное он делает за вас! Не нужны изнуряющие тренировки и диеты!
Есть два подхода — декларативный и процедурный

на какой джуна подсадишь, на какой сам сядешь?
...
int x = Math.between(5, 9, 13);
if (/* Надо ли? */) { System.out.println("x=" + x); }
...

Процессор в обоих случаях найдет значение 9.

Рантайм джавы достаточно умный, чтобы произвести оптимизацию и вычислить флаг заранее, поняв при этом, надо ли считать x

Предлагаемое напоминает борьбу с ветряными мельницами. Примеры непрактичны.
Похоже на попытку внести фанатичный ФП-подход и почти ленивые вычисления в императивный чисто-ООП язык. Даже непонятно, почему нет перехода к lambda-функциям, которые появились ещё в java 8.


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

Достаточно написать статический метод compose, или даже взять существующий Function#compose() или Function#andThen(), и статические (и нестатические) методы чудесным образом начнут превращаться в объекты комбинироваться!


import java.util.function.*;

class Main{
    public static void main (String[] args) {
        Function<Double,Double> cos_sin = ((Function<Double,Double>)Math::cos)
                .compose(Math::sin);
        System.out.println(Math.cos(Math.sin(0.5)));
        System.out.println(cos_sin.apply(0.5));
    }
}

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


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

UFO just landed and posted this here
Мда, «ООП головного мозга», оно такое))
Это же тот мракобес, который любую проблему решает еще одним декоратором:
image

Он в свое время знатно всех в twitter.com/backendsecret повеселил, тоже эту книжку все пиарил.
names = new Sorted(
    new Unique(
        new Capitalized(
            new Replaced(
                new FileNames(
                    new Directory(
                        "/var/users/*.xml"
                    )
                 ),
                 "([^.]+)\\.xml",
                 "$1"
             )
         )
     )
);

Считаете ли вы этот код чистым и простым для понимания?

Я взгляну на этот код со стороны C#, там такое тоже встречается. Знаете, в чем проблема примера? Он не дает контроля. Например, я не знаю, полные имена выбираются или нет. Не знаю порядок сортировки. Приходится опираться не на дефолты стандартной либы, а на дефолты реализации оберток «Sorted» и «FileNames». Тот же самый пример можно переписать с использованием стандартных средств C#:
names = Directory.GetFiles()
  .Select(file => file.Name)
  .Select(fileName => fileName.Replace(...))
  .Select(proceedFileName => proceedFileName.ToUpper())
  .Distinct()
  .Sort();


Другая проблема: зачем называть переменную names? Почему просто не дать осмысленное название переменной и избавить меня от необходимости читать всю эту лесенку?

Наконец, зачем вставлять регулярки в пример «чистого и простого для понимания кода»? В реале подобная замена будет вынесена в отдельный метод с нормальным названием.

Думаю, для холивара этих замечаний будет достаточно.

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

Когда материал подается прямо, в ультимативной форме, его гораздо проще воспринимать объективно. Видя ничем не прикрытые тезисы автора, проще указать на ошибки, проще объективно аргументировать, проще делать выводы для себя. Когда материал подается как «ну типа каждый подход имеет право быть в определенном контексте, блаблабла», начинаешь невольно такой материал трактовать в силу своего опыта и понимания, искажая его по пути.

Для хейтеров такого подхода я рекомендовал бы его же книгу «256 Bloghacks», где он описывает почему он так делает. Пара цитат:

«I am not afraid of being wrong. That's what attracts readers and provoke them to think. I am not just saying what I think, but I provoke them to agree or disagree with me. Sometimes I am wrong. But it's better to be wrong than to say nothing. If I think something about something I just say it, strongly taking one side of discussion».

«It's their (the readers) job to find truth in the middle, not mine. My job is to strongly and explicitly defend one side of the argument. They will find another side somewhere else, they'll compare, and they'll draw their own conclusions».
Главное, чтобы автор не преподавал в ВУЗе где-нибудь

Думаете, там можно что-то испортить?

Егор Бугаенко? Тот самый сексист который считает что женщинам нельзя работать в айти?

Его уже переубедили, и он уже извинился. Вот за EO он еще не извинился, за это можно ругать.

   CMP AX, BX
   JNAE greater
   MOV CX, BX
   RET
greater:
   MOV CX, AX
   RET


Автор:
… Это ассемблерная «подпрограмма» для процессора Intel 8086… Она находит и возвращает большее из двух чисел. Мы помещаем их в регистры AX и BX соответственно, а результат попадает в регистр CX. ...


JNAE — Jump short if not above or equal.
В регистре CX будет возвращаться меньшее значение, а не большее.

Из практического любопытства проверить это можно здесь: http://carlosrafaelgn.com.br/asm86/
предварив ассемблер выше загрузкой в регистры
   MOV AX,2
   MOV BX,3

и посмотрев что вернется в CX…
Ни одного аргументированного комментария. Если поискать, то не один Егор говорит о том, что статические методы — это плохо. lmgtfy.com/?q=why+static+method+is+bad
Использование статических методов, переменных навязано извне и мы в своем коде, думаем, что это нормально и продолжаем использовать статику. Однако это процедурный стиль из 70-ых годов ( dl.acm.org/citation.cfm?id=953355 — в 73 уже поняли, что это плохо), которому пора на свалку.

Вы можете говорить, что у вас в проектах статические методы, глобальные переменные, синглтоны, но тогда расскажите, как вы тестируете свои проекты, как вы их расширяете, как поддерживаете качество кода? Уверен, что вы не писали хорошие тесты на свой проект, вместо расширения существующего, вы переписывали все заново (потому что тяжело расширять или даже невозможно), а о качестве и речи быть не может (ну кроме ревью реквестов, и то не у всех).
Ну например, не вижу больших проблем в веселом классе java.lang.Math, который весь из статиков. И что-то не вижу ему альтернативы. Или у вас таки проекты без математических функций?
Конечно, есть проекты с работой над числами!
github.com/yegor256/cactoos/tree/master/src/main/java/org/cactoos/scalar
— можно вот это использовать для операций над числами, это и гибче, и быстрее
Сильное заявление, проверять его вы конечно не будете?
Уверен, что вы не писали хорошие тесты на свой проект, вместо расширения существующего, вы переписывали все заново (потому что тяжело расширять или даже невозможно), а о качестве и речи быть не может (ну кроме ревью реквестов, и то не у всех).

Диагноз по фотографии?
1) Никто не говорит, что статические методы/переменные есть хорошо. Все и так в курсах, что их нужно стараться делать поменьше.
2) Хватит уже строить демагогию на ложной связи «глобальные переменные — процедурное программирование».
3) Аргументированных коментариев полно. Например вы ответили (судя по всему ошибочно) на комментарий, где указали на явную ошибку в статье. Конкретную. Куда уже аргументированнее я не знаю.
4) Сама статья страдает с аргументацией. Как тут выше написали «А мне как раз больше нравится именно категоричный стиль изложения, как у Егора». Достаточно сложно выступать с аргументированной критикой на такие статьи.
Вот наиболее эффективный и целесообразный способ спора в таком случае. Замеьте, рейтинг у комментария в разы больше, чем у самой статьи ;)
2) Почему глобальные переменные это не процедурное программирование? Есть доказательство?
3) Да, я ошибочно ответил, просто когда я отвечал, это был последний комментарий, и я по ошибке нажал на ответить, а не добавил новый комментарий. Классно, что нашли ошибку в статье, но она никак не связана с огромным неконструктивным негативом в других комментариях.
4) В статье каждое утверждение доказывается каким-либо образом, примерами, логическими связями

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

Только, пожалуйста, не приводите в качестве доказательство обсуждаемую книгу и статью :D

3) Ок. Самый первый комментарий к статье — предложенное решение слишком неэффективно в плане скорости. Другими словами, предложенная методика совершенно неприменима на практике в Java. Это достаточно конструктивно для вас?

4) Ложь. Вся статья по сути набор ультимативных заблуждений автора в стиле
Я думаю, что декларативный код окажется быстрее. Он лучше оптимизирован.
Не, ну если для вас «я думаю» это доказательство, ну ок…

Потому что глобальная переменная противоречит DI (dependency injection). Использование глобальные переменных снижает возможность переиспользования кода. dl.acm.org/citation.cfm?id=953355 — вот классная статья, почитайте!
Как я понимаю вы ответили на пункт 2. Отлично, я рад что по остальным пунктам у вас не осталось комментариев.

2)
Потому что глобальная переменная противоречит DI (dependency injection).
Допустим. Только вот DI != ООП.
Использование глобальные переменных снижает возможность переиспользования кода.
Снижает. Только вот это относится не только к ООП, но и к процедурному программированию.
dl.acm.org/citation.cfm?id=953355 — вот классная статья, почитайте!
Круто, обязательно почитаю!

А по сути то добавить есть что?
Тот способ, на который вы указали, является эмоциональным выражением и больше ничем.

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

Очень плохо, что такие «книги» выпускаются в свет. Очень, очень плохо.
Человек пропихивает в массы крайне мракобесные идеи.
Почитайте хотя бы вот.
Я как раз поддерживаю Егора, его идеи и взгляды мне нравятся и я их использую каждый день :)
Что такое статические методы?
Статические методы подразумевают за собой очень простую вещь — случаи,
когда не нужен экземпляр класса (т.е. объект)
. Возникновение подобных случаев чаще всего обусловлено работой со встроенными примитивными типами.

Статические методы имеют особый выделенный опкод JVM, обрабатываются отдельно, работают быстро, и являются вероятным кандидатом на инлайнинг.
Соответственно, смысл использования статических методов такой:
1. Отсутствие объекта. (т.е. они не пересекаются с ООП-парадигмой***)
2. Скорость.

***
Это справедливо и для C++. Причем, ООП подразумевает за собой именно наличие объектов, а не просто размещение функции в class-scope. Для Java последнее обусловлено самой идеологией, когда бинарно/физически ничто не может существовать вне .class-файлов.


Как только люди начинают смешивать статические методы и объекты (т.е. в процедурную привносится объектная парадигма) — это приводит к некоторым проблемам.
Но это вина не самих статических методов, а смешения.

Было бы лучше указать на проблемы смешения/пересечения статических методов и объектов, а не обвинять их в духе что они «зло» или даже «статические методы в любом контексте — безошибочный индикатор плохого программиста, понятия не имеющего об ООП», «Мы должны прекратить применять статические методы.» и т.д.
int x = Math.max(5, 9);

«Это совершенно неправильно и не должно использоваться в настоящем объектно-ориентированном проектировании.»


Дело в том, что эта строка не объектно-ориентированная вовсе. Вот именно ООП тут нет.
Не надо за него принимать scope функции max.

Стоит ли использовать процедурную парадигму для вычислений примитивных типов?
Вполне. Это по-первых минимально достаточно, и во-вторых обеспечит необходимую скорость. Почему всё живет в Math? Потому что это не ООП, это не объект, а не более чем scope, своего рода «исторический namespace». Именно так это и надо понимать.

Функцию max, работающую с примитивными встроенными типами просто некуда было приписать в Java, которая опирается на устройство java-машины, в оригинале, java-процессора, выполненного в железе. Вне классов ничто существовать не может. Да и как это вообще, могло выглядеть в те годы, в середине 90х?

Статические методы можно рассматривать как преимущество, как возможность использования чисто процедурной парадигмы программирования. Зачем она может понадобиться внутри ООП-парадигмы?
При портировании программ кода с семейства языков C, C++.
При организации эффективных вычислений.
Для run-time API, при связи Java-программ с внешним программным обеспечением, таким как run-time библиотеки, функции ОС, для связи с внешней эффективной реализацией самих Java-библиотек. Это фундаментальная основа j2me, а также различных embedded java-машин, скажем в автомобилях концерна Volkswagen.

Таким образом, приносить в жертву статические методы пока что рановато. Надо просто применять их при достаточной необходимости и по назначению.
int x = Math.max(5, 9);

Это совершенно неправильно и не должно использоваться в настоящем объектно-ориентированном проектировании.

Интересно, а если бы создатели Java сделали поиск максимума арифметической операцией наравне со сложением и умножением, то переходить к new Max() уже не нужно было бы?
Или тогда уже нужно было бы переходить и к new Plus() и new Mult()?

А если бы у вас была функция + и функция -, на которую можно было бы ссылаться и передавать как аргумент другим функциям? Ну и в целом если бы Java позволяла оперировать такими понятиями как функция.

А если бы… главный аргумент, Егор хочет перевести все в ООП, но так ли это важно? неужели все является объектом? или операцией?, она тоже существуют как объект? не даром существуют такие паттерны как «Transaction Script». Вот если честно pattern matching в ФП(хотя я не силен в ФП, пока на стадии обучения) напоминает утиную типизацию,… но все переводить на процесс вычисления, как-то странно, как и наоборот… И дело далеко не в парадигме, а скорее в понимании кода, не понимаю и вряд ли пойму этого фанатизма, а так же кода, ведь давно существует принцип KISS
неужели все является объектом? или операцией?

Для этого надо погрузиться в историю и вспомнить откуда пошла фраза everything is an object. А пошла она из публикации Early history of smalltalk. Это был один (только один из 6-ти) принципов по которому проектировался smalltalk 78.


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


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


То есть суть не в том что бы все было объектом, а в том что бы от данных избавиться. Дальше вся система выстраивается по принципам actor model. То есть правильнее называть "тру ооп" не парадигмой а моделью вычисления. Причем внутри объекта вы можете использовать что ФП (Erlang как пример) что классические процедурные подходы к которым все привыкли.


По источникам если вам интересно, можно начать вот с этого, продолжить на c2.wiki, посмотреть общение автора термина ООП и автора Erlang и о том как они восхваляют prolog и lisp и в крадце рассказывают историю ООП и отношение к нему (скажем последние лет 20 Алан больше в Meta языки ударился).


Вот если честно pattern matching в ФП(хотя я не силен в ФП, пока на стадии обучения) напоминает утиную типизацию

Ну вы очень упрощаете, да и что плохого в структурной типизации? У номинальных систем типов есть свои ограничения. Главное что бы язык позволял вам работать с этими конструкциями с сохранением максимальной информации о типах (а это прощай мутабельность).


а скорее в понимании кода

в том то и суть, объекты Алана — это немного выше уровнем нежели код. Представьте это как модуль, подсистему, сервис или, что там модно нынче, микросервис.


ведь давно существует принцип KISS

Существует распространенное мнение что KISS о том что бы делать то, что первое пришло в голову. Однако KISS это больше о проектировании систем так, что бы эту систему было просто эксплуатировать. В силу простоты принципа наверное, что я нахожу ироничным.


если мы возьмем предложенный Егором и пересмотрим синтаксис то что мы можем найти. Возьмем его пример и просто переложим синтаксис на другой язык — например Kotlin. У нас исчезает оператор new и код становится уже чуть менее "странным", такой код мы могли бы увидеть во многих языках (например javascript).


Что мне кажется странным так это то, сколько Егор тратит усилий, пишет статьи, общается с людьми. Да блин, у него в блоге как-то раз сам Алан Кей отписывался в комментах на темы "что он имел ввиду". Однако всякий раз он опять все путает и делает обработку данных на объектах, хотя идея объектов выше. А его проблемы в целом решит любой функциональный язык.

Чего тут смеяться, Егор последователен. Сказал только объекты и вот — только объекты. Смешно было бы если бы SumOf не было ))

Sign up to leave a comment.