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

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

Наконец кто-то в этом признался!
Поддерживаю!
Придется согласиться — ООП действительно излишне сложен.
Единственное, что в нем просто — точка входа.
Обманчивая простота ООП больше привлекает начинающих программистов, нежели «сложное» функциональное программирование.
Так что обучение, думаю, все равно будет идти по пути «процедурное программиование — ООП — функциональное программирование».
Мне кажется, выбор парадигмы программирования сугубо личное дело.
Лично для меня таскать структуры туда-сюда, как например в C, тоже не комильфо.
Для других, возможно, ОО-подход сравним с демонизмом.
Согласен, просто как бы это двусмысленно не звучало — надо знать меру. Конечно, на мой взгляд, не все надо делать объектами просто потому, что большие дяди так сказали. Хотя, безусловно, дядей надо слушать и много читать, но и своя голова на плечах должна быть.
Возможно, это потому, что вы не вышли за пределы императивной парадигмы.
Есть куда более ясное послание, старое как божий день:

Почему объектно-ориентированное программирование провалилось?

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

Главное не бить себя в грудь и доказывать «ООП рулит»\«ООП говно», а четко понимать цель той или иной задачи и грамотно выбирать инструмент.
Пожалуйста также укажите свой опыт в годах в ООП.
Меньше чем у автора топика. Порядка 5 лет.

Это имеет значение?
На мой взгляд, как-то странно мерить опыт использования парадигмы (да и в принципе опыт) в годах.

Человек за месяц может столкнуться с таким набором различных задач, который у другого наберётся лет за 10 (утрирую).

Автор, к слову, не упоминает очевидный факт — в своё время он отказался от функционального подхода в пользу ООП, на котором «просидел» 17 лет.

Я же соглашусь и чуть дополню Vendolin — для решения разных задач разумнее использовать подходящие для этого средства, а не тупо следовать «принятой» веры методологии.
А, да, если вас интересует мой опыт ООП в годах — 10 лет.
простите, вы уверены что от функционального а не процедурного он отказался? До ООПа доминирующей, если я правильно помню, была парадигма процедурного программирования (c, pascal, etc), а функциональное вроде никогда в мейнстриме не сидело…
Ошибся, но суть не в этом, а в том, что в своё время автор отказался от используемой им парадигмы в пользу ООП.
Mixins тоже являются частью ООП и с ними, как заметил автор, жизнь становится легче. Так что в целом от ООП отказываться смысла нет, но сделать ЯП более гибкими не помешало бы.
М, хотелось бы конкретики и примеров. Вроде «вот такая штука сделана на ООП и она не работает, а вот штука сделана на Scala и она правильная и хорошая». Может не сюда, в виде отдельной статьи. Неужели все многочисленные стандартные библиотеки для ЯВУ, сами ЯВУ в основе которых лежит ООП, фреймворки для создания графических интерфейсов, веб-приложений, ещё море библиотек, которые в подавляющем кол-ве случаев написаны на ООП и которые используются очень широко, и продукты написанные на которых отлично работают и развиваются, что все они «неправильные», все «не работают»? С трудом в это верится.

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

Ну а если это действительно так и ООП это одно расстройство — хотелось бы примеров что бы убедиться в этом, и что бы другие разработчики в этом убедились, что бы в конечном итоге настало светлое будущее без гадкого ООП :)
Любая штука работает на ООП. Проблема в том, что код при этом многословен и запутан. По сравнению с другими парадигмами. Нет ни одного примера «тут не работает, а вот тут работает».
Речь не о том, работает ли по факту или нет. Думаю, что race1 под «работает» имел в виду именно «приносит конкретные бенефит».

То, что «под на ООП запутан и многословен» — абсолютно ни к чему не привязанное утверждение. Есть задачи для ООП, а есть те, которые с ООП не нужно пытаться решать.
«Любая штука работает на ООП. Проблема в том, что код при этом многословен и запутан.»
А можно примеры? Того, что _любая_ штука на ООП многословна и запутана.
Я не говорил что любая штука на ООП многословна и запутана. Но исходя из опыта я считаю что чаще всего это так. Как минимум ещё несолько человек считают так же. При желании посмотрите ссылки что привели комментаторы выше, очень интересный материал.
Ссылки — это «why OOP failed»? Этому тексту стопятьдесят лет, и он с тех пор не стал более аргументированным или менее демагогским.

«Но исходя из опыта я считаю что чаще всего это так.»
Примеры-примеры-примеры. Без примеров это все пусто.
В первой ссылке вроде примеры.

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

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

Так что за примеры того, что «чаще всего это так» не работает.
Александр Степанов эту сложность объясняет примерно так:

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

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

Ну и надо понимать, что в реальности «программирование» начинается с определения задачи, а она может определяться в различных терминах, и далеко не всегда это термины алгоритмов. Бизнес вообще редко мыслит алгоритмами.
Но реализовываете-то вы любой бизнес именно алгоритмами?
*реализуете(?)
бизнес как-бы с алгоритмами и рядом не стоит ;)
компания может придерживаться жёстких правил, а может и «в свободном плавании» жить ;)
«Но реализуете-то вы любой бизнес именно алгоритмами?»
Совершенно не обязательно. В частности, декларативно программирование — это не реализация алгоритма.
Оффтопик, но что-то я не припомню, чтобы в математике все _заканчивалось_ аксиомами — не задавшись набором аксиом, можно получить все, что угодно, хоть 2*2=100500.
Касаемо ООП — в моей практике были и те, и другие примеры проектов. Т.е. которые, соответственно, хорошо или плохо ложатся в ООП парадигму, 50:50 где-то. Причем независимо от того, бизнес проект это или pet.
Речь не идет о том, как преподают некую теорию в ВУЗах, там да, сначала нам дают набор аксиом, после чего на основе их выстраивают стройную теорию. Дело в том, аксиомы получались не сразу, но после того как приходится работать с математическими объектами. Как пример, операции сложения и умножения, их коммутативность и ассоциативность появились задолго до того, как были написаны аксиомы поля над вещественными числами; были известны некоторые свойства вероятноти — такие как вероятность объединения 2х событий или пересечения — до того как сформулирована аксиоматика Колмогорова.
Т.е. цель свести все к стройной системе, построенной из каких-либо независимых автономных блоков, очень сложно достижима на начальном этапе.
Поддерживаю. Такие заявления без практических примеров ничего не стоят. Слишком абстрактно все.
Не будьте столь категоричны. Любую программу можно написать на ассемблере (и даже будет работать), вопрос в удобстве.

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

Да, там есть замыкания (и в java будут), там есть лямбды, но каждый раз создавая лямбду вы можете полагать (и будете почти правы), что используете синтаксический сахар для упрощенной записи анонимного наследника FunctionN с реализацией метода apply.

И так со всеми функциональными конструкциями (даже patern matching в 2.10 сделают сахаром, преобразуемым в набор вызовов определенных методов).

Некоторые (например я) считают, что scala гораздо удобнее, чем java, но даже если принять такую точку зрения, то я не представляю как тут можно сделать вывод о провале ООП.
Scala несет себе много фундаментальных отличий и большое количество улучшений, синтаксического сахара.
Но, к сожалению, в Enterprise системах ее еще несколько больно использовать. В частности из-за еще относительно малого количества стабильных библиотек, которые были написаны специально для Scala. А использовать Scala вместе с Struts2, Spring Jdbc, Hibernate. Это будет намного больнее и уродливее, чем написать это просто на Java.
Чем это использование Hibernate в Scala больно и уродливо?
Например конвертацией Java'шных коллекций туда и обратно. Я знаю, про implicit конвертацию из/в java'шные коллекции. Но это тоже больно. Приплюсуем сюда struts2 с желанием итерироваться из JSP с помощью тега <s:iterate /> и получим совсем не клевую ситуацию, где нам приходится или проходить путь:
java collection -> scala collection -> java collection. Или же в каждом конкретном случае смотреть, стоит ли коллекцию конвертировать из Java в Scala или нет.

Помимо того добавим сюда тот факт, что на прямую мы не можем использовать Scala'шный Option и поэтому в POJO нам приходится изворачиваться и писать что-то в роде:
import java.math.{BingInteger => JBigInt}
import java.lang.{Long => JLong}

class Pojo {
  <hh user=BeanProperty> var id: JBigInt = _
  <hh user=BeanProperty> var name: String = _
  <hh user=BeanProperty> var count: JLong = _
}


вместо желаемого

class Pojo {
 var id: Option[BigInt] = _
 var name: Option[String] = _
 var count: Option[Long] = _
}


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

Но, к сожалению, я не очень хорошо ориентируюсь в Scala мире. Возможно есть фреймворки, которые либо грамотно оборачивают вышеуказанных монстров (Hibernate) либо же предлагают хорошую альтернативу (я слышал о веб-фреймворках Lift и Play, но, к своему сожалению не успел познакомиться с ними еще, так что не могу прокомментировать).
Очень рекомендую обратить внимание на Play 2.x. Он изначально проектировался как скала фреймворк.
Работа с БД там не через ORM, у них своя прослойка в функциональном стиле:

— Using JDBC is a pain, but we provide a better API
— You don’t need another DSL to access relational databases, SQL is already the best DSL
— A type safe DSL to generate SQL is a mistake
— Take Control of your SQL code

Если вас не оттолкнули эти принципы, то по ссылке подробней о каждом: www.playframework.org/documentation/2.0.1/ScalaAnorm
Отличнейший пример валидации в функциональном стиле с помощью Scala + scalaz gist.github.com/2660512
А теперь представьте, сколько кода надо было бы написать в обычном стиле на типичном ЯП, чтобы добиться такого же результата.
С первого взгляда не очень понял что там и почему в обычном стиле это не будет просто :)
Комментарии в коде все доходчиво объясняют ;)
Не лучший пример.
Всего-то раза в 2 больше, не более того.
А при увеличении количества валидаций разница в количестве строк становится несущественной.

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

И даже в случае с ФП: на java можно писать в ФП стиле используя анонимные классы. И даже суррогат замыканий там можно делать самому.
Это будет многословно, это будет не красиво и противоестественно для тех, кто знаком с ФП языками. И разные библиотеки будут создавать разное ФП. Даже разные программисты на одном продукте могут, не заметив, создать разные наборы классов для реализации элементов ФП.

Хотите хороший пример? Напомните про guava. Вот только надо еще доказать программисту, использующему guava, что он пишет с использованием ФП. После этого останется только показать как то же самое пишется с поддержкой ФП в языке.
НЛО прилетело и опубликовало эту надпись здесь
Что забавно, так это то, что, если ООП код писать, следуя принципам SOLID, то в конечном итоге мы приходим именно к функциональному программированию :-)
А если покопаться в ФП скалы, то мы поймем, что там везде ООП. Причем в самом классическом его понимании.

Противопоставлять ФП и ОПП — это как сравнивать теплое с мягким. Они ни как друг на друга не влияют.
Ну так вся фишка скалы — в конвергенции ООП и ФП. Чего еще ждать от языка который бежит поверх ВМ в которой все пронизано объектами, при этом желая интеграции с существующим кодом?
Года четыре назад я тоже увлекался функциональными и прочими языками программирования, и понял, что не смогу смириться с их отдельными недостатками. И пока я буду писать приложения на стероидах, они будут на С++ с правильными классами и широким использованием паттернов.
С каждым разом все больше убеждаюсь, что подобные статьи читать в непрофессиональном переводе нет никакого смысла. Формулировки вроде понятны, а чтобы добраться до смысла, приходится перечитывать по два раза. Прочитал первую треть, плюнул и пошел читать оригинал.

За ссылку спасибо, за перевод не зачет.
НЛО прилетело и опубликовало эту надпись здесь
Вопрос не в том какой инструмент использовать, а как понимать задачу. Плохой код можно писать на любом языке. К тому же, использование функций вместо объектов, приводит к излишней фрагментарности кода.
Согласен с автором ) мы загоняем себя в жесткие рамки, а потом сидим и чешем репу, как же их обойти не написав кривой кастыль :(
А с автором оригинальной статьи можно как-то связаться?
Я прошел по ссылкам за вас. У него есть твиттер и блог. Где-нибудь, да ответит. www.blogger.com/profile/09141108967081921802
спс
Мне лично ООП помогает поддерживать код. Плюс показывает, насколько хорошо я понимаю предметную область, насколько структурно мыслю.

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

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

А паттерны — это просто кусочки, вырванные из контекста. Вот смотрите, есть у нас принцип — инкапсуляция. Есть рекомендация, вытекающая из него — низкая связанность. Есть два принципа SOLID, в паре работающие на низкую связанность прямым образом — это DIP и SRP. И из этих двух принципов вытекает конкретный паттерн стратегия, который решает вполне четкую __локальную__ задачу, но является частью СИСТЕМЫ.

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

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

То есть если не ясно, какие есть объекты и отношения между ними, то суть не схвачена.

Пример. Если вы пишете Crm, и у вас еще нет ощущения, что нужна сущность клиент, причем сложная (составная) — прекратила писать код и получше изучите предметную область.

Не все можно смоделировать инструментами ООП, но очень многое
В старые времена, когда не было такого термина «Code First» мы разлагали предметную область на таблицы БД и связи между ними — это называлось ER диаграммами. Обратите внимание, что в большинстве, как мне кажется, систем, таблицы и классы-entity мапятся один к одному. Так что, когда вы разлагаете предметную область на entity, это не ООП, точнее не обязательно ООП.

Вы упомянули о бизнес-процессах. Есть такой «процессный подход к управлению», я думаю вы знаете об этом, был популярен в консалтинге какое-то время назад (может и сейчас). Там в основе всего – процессы (процесс Заказа, процесс Отгрузки и т.п.). Такие вещи, как записи в базе данных или бумажные документы, считаются артефактами процесса. В связи с использованием ООП (и других частных методологий типа DDD) внимание сместилось в сторону артефактов (1С, например, называет себя «документо-ориентированной» системой). В результате в системе вы можете найти таблицу «Заказ», но вы нигде не найдете «Процесса Заказа», он размазан по всей системе.

И еще важный момент — модель (не смотря на спекулятивное использование этого термина) — это не entity — это некий набор алгоритмов, функций.
Есть ещё мнение, что некоторые программисты любят привязаться к старой методологии потому что та методология была первой что они узнали и от старого коня отказываться трудно, но прогресс ушёл вперед. Раньше были первичны данные(те же таблицы) и тогда и до сих пор некоторые отталкиваются от них. И это было правильно на тот момент. Примерно также, как то, что землю можно было считать плоской. А теперь мы знаем что земля круглая и плоской она кажется лишь потому, что мы очень маленькие. И теперь первичные не данные, а предметная область в целом, которая рождает две низкосвязанные части: базу данных и её связи (сделанные по принципам построения БД) и модель предметной области(домен) в коде по принципам ООП (в т.ч. и процессы и сущности и агрегаты) Но т.к. всякие сайты визитки слишком просты, чтобы изучать подобный подход, а старые системы те вообще и ООП-то используют лишь наминально, то появляется большое кол-во споров, причина которых — недопонимание сути вопроса.
В свое время писал отталкиваясь от данных, несколько лет писал по книжкам DDD, даже считал это подход вполне адекватным. Но — полезно знать много технологий, чтобы сравнивать и делать выводы. Разработка набора entity в DDD и разработка таблиц БД практически не отличаются по сути — как я уже выше сказал — модель базы данных раньше и называлась ER диаграммой (Entity relationship). А процесс разработки этих самых entity начинается с анализа того, что происходит в бизене — то есть с процессов. Я не заметил в DDD методике фокуса на процессы — во всей книжке Эванса нет ни одной activity диаграммы или workflow.
Да, так что я хотел сказать. Из тех технологий, с которыми я работаю, программные сущности больше всего отвечают реальным сущностям в такой штуке как OLAP кубы. В базе данных может быть legacy структура таблиц, по большому счету руководству предприятия плевать какие там классы в программе. Но когда менеджер таскает dimensions и measures в сводной таблице excel — они не могут не соответствовать его бизнесу — иначе он не поймет данных отчета.
Многое в ООП — это не плохо, но это чисто инфраструктурные вещи и ошибка полагать что DDD помогает лучше понять или отобразить предметную область — это просто еще одна разрекламированная patterns and practices.
да можно вообще писать всю логику в БД. та же Postgre это позволяет.

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

2. честно, хз. я плохого мнения об образовании по менеджменту в РФ. у нас нет гениальных школ, таких, как сделали в GE люди с Джеком Уэлчем, и выдающиеся бизнесмены типа Галицкого пока не преподают в вузах (и вряд ли будут).
равно как и кейсовый подход.

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

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

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

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

3. вообще за слово «модель» нужно оторвать кому-то руки.
я поэтому сейчас за DDD. там есть четкие понятия. есть сущность — объект без состояния, это тупо класс со свойствами и базовыми методами (к примеру, у комплексных чисел метод — принять другое комплексное число на вход и отдать результат умножения новой сущностью на выход — хотя если «ооп ради ооп», то нам уже нужен оператор умножения N комплексных чисел), есть класс-коллекция, есть storage, который по заданным условиям вернет коллекцию объектов-сущностей.

а MVC это спорный паттерн. щас попробую топик зарядить.
Вот видите, вы все без меня знаете на самом деле :)
Особенно доставляет, когда адепты Java говорят адептам C#, например: «создавать инстанс генерика в обобщенном типе» неправильно и не по дзену — «если в c# так можно, это не значит, что это _правильно_». — а нужно всего лишь выделить абстрактную фабрику.)
Многие так называемые паттерны — лишь набор задокументированных ворк-араундов вокруг лимитаций наложенных языком. Так что я бы забил и радовался, если C# позволяет такое писать.

У меня в Java такого нет и, поэтому, я буду дальше продолжать писать абстрактные фабрики. Не потому что это _правильно_, или клево, или мне это нравится, или я получаю удовольствие от усложнения кода, а потому что язык, на котором я пишу каждый день накладывает на меня такие ограничения.
Главное заблуждение статьи, что она названа «Жизнь без объектов», а критикуются в ней ООП. Абсолютно неверно. И в конце еще приводится фраза, применять mixin traits вместо наследования. Так к чему их применять?

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

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

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

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

Так, как писали до ООП. Модульность не в C++ придумали.
Было бы странно если автор это имел ввиду, приводя в пример Scala, который даже если не учитывать что сам объектно-ориентированный, вдобавок ещё стоит на JVM, которая объектно-ориентирована вообще насквозь.
В чём состоит «объектно-ориентирована насквозь» для работчика, который использует всё это просто как библиотеку? Если Вы хотите использовать функцию из libc, то это можно сделать в ассемблере, в Си или в руби, какая разница, как эта функция реализована внутри?
Есть разница какой интерфейс она имеет, особенно если язык с этим интерфейсом напрямую работать не может, ну нет у него в синтаксисе понятия функция, есть только методы, принадлежащие классам.
Да, разница в синтаксисе, согласен. Но и только: если надо вызвать некоторую функцию X из модуля Y, то программист просто сделает `result = Y::X(args)`, а не будет задумываться, мыслил ли автор X концепциями объектов и сообщений, или же просто использовал классы как модули. Какое это имеет значение для того, кто просто вызывает библиотечную функцию?
Ну вообще, методами написания совта до C++, софт получался сильно дороже и намного менее надежным, чем сейчас. Вспомните windows, word, и прочую хренотень.
Парадигма программирования — далеко не самое важное, что влияет на цену и надёжность. Чёткие спецификации, хорошая архитектура, автоматическое тестирование — всё это не зависит от парадигмы.
Но если уж на то пошло, то и до плюсов были целиком объектно-ориентированные языки, тот же Smalltalk.
Я не имел ввиду конкретно C++, а ООП в целом. Прсото C++ это первый широко используемый объектно ориентированный язык.
НЛО прилетело и опубликовало эту надпись здесь
Скажите об этом Алану Кэю
Модульность, это важно, но не самое главное. Вообще, странно приводить в пример Scala, которая обладает как раз очень мощной объектной системой в статье с таким названием. Зато если взять Clojure, то там как раз действительно не будет объектной системы в привычном по ++/#/J понимании. Взамен этого будут полиморфные функции, мультиметоды/протоколы и прочее. Отказ от ООП не означает возврат к процедурному стилю, не путайте его с ФП.
Это спор о терминологии. Очень долго термин «ООП» означал именно такую реализацию, которая привычна по C++ и Java. Только в последнее время, в связи с широким использованием Javascript в вебе и вне него, начинают говорить, что ООП бывает разное.
Вы же сами упомянули выше про Smalltalk. Неужто там такое же ООП как в C++/Java?
Как сказал Алан Кей, автор Smalltalk'а:
Actually I made up the term «object-oriented», and I can tell you I did not have C++ in mind.
под понятием реюзабельный подразумевается что написанный класс использовать при написании 10 раз. Однако даже при легком изменении логики все иерархию как правило переписывают. В итоге бенефит сомнительный. Для использования пары повторяющихся методов выдумывают сложные иерархии классов. Настала версия 2.0 и все полностью переписывают заново. В этом смысле примеси немного облегчают жизнь, да.
Если вы не умеете пользоваться ООП для реюзабельности, не означает что ООП плохо. Просто им, как и всяким инструментом надо уметь пользоватсья.
Тут скорее не в инструментах дело, а в предвидении будущих задач, которые зачастую ещё не существуют. Когда разрабатывается одновременно два похожих проекта, то выделить общую часть для всех проектов такого класса не сильно сложно. А когда проект один и он первый в своем классе, то можно только гадать какая его часть может оказаться востребованной для повторного использования и имеет смысл тратить ресурсы на её универсальность и изоляцию (или хотя бы иметь в виду такую возможность), а какая будет уникальна и эти ресурсы будут потрачены зря.
То что вы говорите не зависит от парадигмы программирование. Если проект пишется кое как, не думая о развитии, и реюзе, какую парадигму авторы бы не использовали, хоть объекто-аспектно-функциональное программирование, получится говнокод.

И наоборот, если понимать требования, куда проект будет двигаться дальше (присутствует стратегия развития), то даже на асме можно добиться реюза, но все таки лучше если это будет современный ОО язык.
Так я и говорю, что не от инструментов (в том числе парадигм). Но суть в том, что даже думая о развитии и реюзе можно не угадать направление в котором проект будет развиваться и какие его части, как и где будут реюзаться. Предусматривать всё — явный оверхид, не предусматривать ничего — сложности при малейшем изменении или попытке реюза. Требуется именно угадать что будет меняться, а что повторно использоваться, чтобы не тратить ресурсы зря на оверинженеринг при начальной разработке и на рефакторинг при развитии и повторном использовании.
Именно поэтому, широко известные в узких кругах гуру советуют делать везде, где только можно минимальную реализация и пользоваться рефакторингом для дальнейшего развития. Так и говорят: «Если у кого и получается предсказывать будущее, то ему очень повезло. А я по старинке»
Не предусматривать ничего. Сложности возникают при несоблюдении процесса — отсутствии рефакторинга. Если же с ним всё хорошо, то:
1. Потраченное время на предугаданную вещь в случае угадывания равно приблизительно времени, которое потратится позже. Когда возникнет потребность.
2. Потраченное время на предугаданную вещь, которая не потребуется (не угадали) остается зря потраченным временем. Плюс к этому добавляется время на поддержку ненужного кода «на всякий случай». Или выпиливания.

Когда процесс налажен, то рефакторинг и тесты писать не сложно и менять код в случае небольших изменений тоже. Ведь если не предугадывать, то код всегда самый простой. А когда люди предугадывают, то у них и мышление построено так: не буду убирать код, который уже написан и еще не понадобился — вдруг понадобится. Тут не до рефакторинга. Получается дедушкин чердак со всяким хламом, а не код.
А по мне, так не всё так просто. Одно дело, когда речь идёт про то, как раскроить иерархию классов, да распихать методы по интерфейсам. Другое — когда принимаются те или иные ключевые архитектурные решения, изменения которых аффектят 90% написанного кода. Ну, например, на начальном этапе принимается решение использовать некое средство X (например, средство доступа к БД). На этот средство завязывается весь код. А потом выясняется, что полученный продукт должен работать на порядок быстрее, и это самое средство X является бутылочным горлышком. Итоговый рефакторинг — это переписывание большей части кода (по сути — всё пишем заново). Но можно было бы поступить и иначе — для средства X написать фасад, и использовать только его. И потом (когда возникнет неприятность с производительностью) — только его и переписать. По-моему, затраты несопоставимые.
Как-то нелогично, когда много работы связывается не с задачами, а с инфраструктурными средствами. Значит средство плохое, если не дает достаточной развязки. Почему код связи с БД должен влиять сильно на остальной код, который на него завязывается? Хотя, бывает, но всё таки не вижу выгоды и здесь в предугадывании. Придет новое требование — перепишем. Гораздо чаще на моей памяти проблемы возникали, когда наоборот — угадывали. Исходя из предположений: а вдруг база поменяется и т.д. Ни разу не менялась база, а вот код доходил до точки, когда у людей, его писавших возникало желание его с нуля переписать. Программисты же всегда оптимисты, если не пишут тесты и рефакторинг. Они всегда переоценивают свои силы. И происходит потому, что не верят, что напишут баги. И вот в «разработке архитектуры» по умолчанию думают, что код будет сразу верный и лучше предугадать и создать нужную гибкость. Я думаю, что даже в случае, описанном вами, когда вначале код завязался на что-то, а потом надо переписать, в большинстве случаев такой вариант окажется менее затратным. Чем поддерживать излишне гибкий код, когда даже угадали и пришло изменение требований и переписывать не надо.
Кто бы такие такие замеры времени делал и делал сравнения? Сплошной оптимизм в планировании. А выводы в случае неудачи планирования всегда обратные: не в том, что планировали преждевременно, а в том, что неправильно угадали.
На самом деле всё просто. :) Всё сводится к оценке рисков и вероятностей. Понятно, что для проектов «сделал и забыл» подходит принцип «Чем проще, тем лучше». Для проектов, которые предполагают длительный период развития, несколько релизов и т. п. — уже нет. Очевидно, что если переписывание кода «потом» будет менее затратно, чем сделать прослойку «сейчас» — то и вопросов нет. Код as simple as possible + тесты. Если же будущий рефакторинг может быть более затратный — то риски просто необходимо оценивать, исходя из текущей ситуации и предположений. И уже по результатам таких оценок принимать решения. И универсальных рецептов тут нет.

Про БД — пример то прост. Ну, например, «сейчас» код пишется с использованием прямых SQL-запросов к базе. Положим, MySQL. А то и SQLite. Прошло некоторое время, нагрузка выросла, выбранная БД не справляется. Что делать? Переписать весь код, который завязан на работу с БД. А это чуть меньше, чем вся бизнес-логика. Это большой рефакторинг или маленький? Было бы лучше, если бы сразу было принято решение использовать какой-нибудь ORM?
Мы сейчас начнем спорить исходя из собственных вкусов и примеров, которые каждый себе представляет :) Скажу только, что я был такого же мнения как и Вы. Но со временем мнение изменилось. Просто по опыту, что случалось, а что нет. У вас может быть другая статистика.
Все эти методики работают только при определенных навыках работы с кодом. Я и в сложных проектах вижу только такой путь — чем проще тем лучше. Не могу себе представить, где это не работает.

Можно в личной переписке погонять какие-то примеры. А то не сможем обоснований найти.

С примером с БД не вижу проблем. Тут надо смотреть. Если логику всю выполняет база с помощью запросов, то видимо слой будет, который эти запросы хранит. Не будут же они размазаны по всей иерархии? Ну и это не является какой-то хитрой архитектурой. После двух запросов и рефакторинга может выделиться объект, который оборачивает запросы в методы.
Далее, если база не справляется, это задача оптимизации. Стандартно и смотреть, как можно это оптимизировать. Кстати, правильный путь — базу грузить работой. Как раз предугадывание того, что база не справится — и попытка на этапе проектирования разгрузить — потенциальные проблемы. СУБД создают для работы. Для запросов. Для масштабируемости. Пусть и делают то, за что ей деньги платят ))
Когда тормозит, то:
1. Пытаемся решить железом. Дешевле.
2. Если не выходит первое, то пытаемся кешировать. Замеры, попытки кешировать, вплоть до того, что что возможно выделится слой кеша на апп-сервере, который будет фреймоврком Unit Of Work.
Можно и второй этап сразу с конца сделать — выбрать подходящий фреймворк (если замеры говорят, что база много где тормозит). Ну и переписывать не должно быть так уж много. Методы уже возвращают списки сущностей или принимают на вход. Переписывается ровно то, что тормозит — логика работы.

Не вижу причин это предугадывать. Да, это может занять время. Но с другой стороны, умные люди СУБД придумывают, как систему обработки больших объемов информации, кеширование запросов и всяческие оптимизации. А я вижу много программистов, которые создают свои велосипеды, которые повторяют чуть ли не всю СУБД. А СУБД отводят роль только файловой системы для хранения состояний между выключениями машины.

И на счет ORM. Какие-то объекты-сущности нужны. По условию, ORM не выбрали, потому как руками писали запросы. Но надеюсь, что какие-то генераторы классов были. Нет, то следующий шаг — сделать. Или может уже выбрали ORM, но тогда запросы в хранимках хранились, а не в коде.

Сразу же выбрать такую ORM, которая будет брать на себя функции СУБД, я бы не стал. Всё нормально с примером.
> Все эти методики работают только при определенных навыках работы с кодом. Я и в сложных проектах вижу только такой путь — чем проще тем лучше. Не могу себе представить, где это не работает.

Собственно, дополняя этот тезис, хочу сказать, что не только при определённых навыках, но и просто не во всех ситуациях. Факторов, на самом деле, много (характер задачи, размер команды, опыт членов команды, перспективы задачи и т. д. и т. п.), и то, что прекрасно работает при одной комбинации этих факторов, перестанет работать при другой. А так… Безусловно — у каждого свой опыт. Мне приходилось работать и в ситуациях, когда постоянный рефакторинг был возможен (и оптимален), а также в ситуациях, когда принятые «сейчас» решения уже нельзя было отменить, или это было ну ооооооочень дорого.
По-моему возвращаемся к необходимости угадывать будем данный кусок кода рефакторить или нет. Если 100% будем — покрываем его тестами или, хотя бы, пишем так, чтобы тестами было легко покрыть. А это иногда (часто?) приводит к усложнению кода. Хотя, конечно, сложность кода понятие относительное. Например, инкапсулированная агрегация может проще с одной точки зрения и сложнее с другой. Создать объект для присваивания приватному свойству в конструкторе просто, отследить эту зависимость нет, а изменить её без модификация собственно кода класса зачастую невозможно или очень-очень сложно. Передавать этот объект параметром конструктору сложнее немного, отследить эту зависимость проще и изменить тоже.
НЛО прилетело и опубликовало эту надпись здесь
для работы с ФП нужна более качественная подготовка программиста как в области математики так и в области теории программирования.

Некоторые примеры показывают, что нужно просто другая качественно подготовка, а не более качественная. При условии если мозг не испорчен императивщиной как у меня :) Более того, ФП более легко ложится на школьную математику без вводящих в ступор «математиков» выражений вроде x = x + 1;. Сами понятия «переменная», «поток выполнения», «ветвление», «цикл» очень плохо вводится без обращения к архитектуре компьютера. «Функция», даже «ФВП» более естественны что ли.
НЛО прилетело и опубликовало эту надпись здесь
Напрасно вы так Haskell дискредитируете. Не нужна там теория категорий.
НЛО прилетело и опубликовало эту надпись здесь
Самое интересное — почему стал возможен такого рода пост. Почему никто не говорит «долой функциональное программирование» к примеру. Как было сказано в посте — это была разрекламированная вещь. Некий продукт мог иметь в 90-е «конкурентные преимущества» уже только потому, что на коробке было написано «ООП». Да и сейчас, если вы устраиваетесь на работу в корпоративный сектор, паттерны спрашивают как катехизис на собеседовании. В этом что-то есть от сетевого маркетинга.

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

А что касается самого по себе ООП, я думаю, уже никто не использует его в чистом виде в новых проектах. Посмотрите open source проекты на C# например — очень многие активно используют возможности ФП в C# (как сказано в посте «Такие тяжелые фреймворки быстро умирают и замещаются более легковесными библиотеками и наборами легковесных библиотек»).
Да и многие ООП паттерны — на самом деле реализация тех возможностей, которые есть в ФП из коробки (strategy например). Паттерн Entity-Service-Repository — тоже нельзя назвать чистым ООП: entity слишком плоские, сервисы и репозитории тоже трудно назвать полноценными объектами — скорее контейнерами с процедурами/функциями.
C# идет по единственно правильному пути, на мой взгляд. Дорогу гибридным языкам!
Иметь доступ ко всем значениям объекта — неправильно, если мы говорим про инкапсуляцию. Кому нужны мои почки и правое легкое, чтобы общаться со мной? :)
Врачу?
Телепорту? Или прийдется ждать пока Вы начнете поддерживать интерфейс телепортации.
Врачу отдельный интерфейс, доступ к которому он получает, применив метод «Наркоз». Телепорт… ну давайте помечтаем :)
Мне кажется телепорт упомянули в качестве неожиданного, заранее не предвиденного использования объекта. С другой стороны, в этом случае лучше дописать поддержку интерфейса телепортации для объекта. Другое дело, что не нужно везде реализовывать все возможные интерфейсы иначе код превратится в раздутую помойку.
Очень странный набор умозаключений.

ООП противопоставляются такие вкусные и полезные вещи, как:
1) ФП. Учитывая приводимую в пример scala ФП и ООП не исключают друг друга.
2) stateless. Среди принципов «инкапсуляция», «наследование», «полиморфизм» нет слова «изменяемость». Если добавить в ООП удобные immutable структуры оно не перестанет быть ООП.
3) Поведенческие классы и классы с данными. Да, не канонический подход, но удобный и давно используемый в ООП.
4) mixin traits. Это удобная реализация множественного наследования. Чем она противоречит принципам ООП?
5) Функции как first-class citizen. Да, удобное и компактное представление для паттерна «стратегия», а так же некоторых других. Да, в ФП языках принято забывать некоторые паттерны, так как их реализация заложена в самом языке. Но это ни как не противоречит ООП подходу и не отменяет наличия в ФП языках своих паттернов (якобы их меньше, но не в этом суть).

Я бы согласился с тем, что ООП + immutable + ФП + некоторые другие вкусности, это лучше, чем ООП без всего перечисленного, но не понимаю каким образом это делает ООП подход устаревшим и не удобным.
Инкапсуляция инкапсулироует именно изменяемые данные. Причём незаметно и неизвестно как изменяемые.
Почему это?
С тем же успехом можно инкапсулировать неизменяемые данные.

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

Простой пример: многие immutable коллекции в scala скрывают свою реализацию и при преобразованиях просто возвращают измененную копию.
C трудом представляю себе ООП с неизменяемыми переменными…
Я тоже с трудом представлял, а потом открыл для себя scala.
Теперь не представляю как работать с многопоточностью не имея в языке удобной поддержки неизменяемых данных. Да и не многопоточный код зачастую становится проще для понимания.

Но я согласен, что полный отказ от изменяемых данных зачастую неудобен (хотя иногда жизнеспособен, взять хотя бы xslt). Авторы ФП языков тоже понимают, потому изменяемые данные тоже поддерживаются.
Спасибо за комментарий, задумался. Правда меня зацепил erlang, на scala уже времени нет)
Это не исключающий случай, эрланг очень маленький и концептуально целостный и требует относительно мало времени на изучение. Чего не скажешь о scala.
Erlang/OTP целостный, но времени отнимает кучу и есть куда копать (в сторону веб дева например). Невозможно постоянно учить новые языки, Asm, C, Ruby, Erlang, JS — что еще может поместиться в голове помимо этого? Есть Clojure, Haskell, Scala и куча других интересных вещей, но увы.
Опять про Groovy забыли…
Но вообще в голове всё поместится, было бы время и желание. Чем больше языков изучаешь, тем легче это даётся, потому что начинаешь понимать как все языки строятся из фич и что во многих из них очень много этих самых одинаковых фич. А чтобы выучить сам синтаксис — где ставить скобочку а где точку — вообще много времени не надо.
Проблема не в синтаксисе а в инфраструктуре. Сами языки учатся чем дальше тем легче, но это разве что в академических целях. Но если работаете С++ программистом — изучите Boost, JS программистом — ориентируйтесь в популярных библиотеках типа JQuery/Backbone/etc (и как на них не просто писать, а писать красиво и общепринятым способом). Платят не за абстрактное знание языка а за умение быстро и понятно написать рабочий код без велосипедов, а это требует долгого времени изучения каждого стека.
Ну я конечно соглашусь здесь с нами, но всё равно, когда знаешь уже несколько языков/фреймворков, остальные даются всё легче и легче, ОСОБЕННО когда речь идёт о фичах в разных языках предназначенных для одного и тоже. (А не так что сравнивается assembly и jQuery).
К примеру если взять PHP, Python и Ruby — и скажем вам сейчас надо написать вебприложение. Вы скорее всего начнёте изучать django/rails/{одна из десятков альтернатив для PHP}, и хоть все языки очень разные с разными стеками приложений и утилит, всё равно сами MVC фреймворки очень похожи, просто потому что разрабатывались для одной цели. Поэтому когда нужно будет изучать grails для Groovy, это уже пойдёт гораздо быстрее, вы просто будете узнавать все фичи.
(но за пару дней конечно можно изучить лишь синтаксис, для эффективности требуется время, независимо от языка).
всё равно сами MVC фреймворки очень похожи, просто потому что разрабатывались для одной цели.

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

P.S. django и rails альтернативы не php, а symfony/yii/zend/cake/kohana/… Альтернативы php — python и ruby.
(PS) я это и имел ввиду «django/rails/{одна из десятков альтернатив [django или rails] для PHP}»

Как раз насчёт того что легко перейти на другой MVC фреймворк легко (а не на другую архитектуру и вообще другой проект/цели) я и писал. О чём мы спорим?
Цель у веб-фреймворков одна — выдать клиенту html (xml, json и т.п. опустим). Но вот подходы к её достижению могут быть разные. MVC один из них, пускай и доминирующий, но не единственный. Взять микрофреймворки типа sinatra или silex — MVC в них нет. Да и если цель другая, например, десктопные приложения, то переход в рамках MVC (не вебом единым) будет проще.

Собственно хотел уточнить, что не то, что цель одна облегчает переход, а то, что архитектура одна.

P.S. наверное надо было написать: «одна из десятков альтернатив на PHP»
Если найдете время на scala, то обнаружите знакомые моменты. В частности модель акторов в scala полностью заимствована из erlang.
Такое ООП есть в OCaml.
>1) ФП. Учитывая приводимую в пример scala ФП и ООП не исключают друг друга.
ООП и ФП не очень уживаются друг с другом. Например отваливается полный тайп инференс, и систему типов приходится делать намного сложнее. Вспомните ужасные? extends и? super из Java. Вобщем-то из вещей на которые часто жалуятся в Скале, самое распространенное это сложная и замедляющая компилятор система типов.

>3) Поведенческие классы и классы с данными. Да, не канонический подход, но удобный и давно используемый в ООП.
Что вы подразумеваете под поведенческими классами и классами с данными? По моему, в ООП уже давно принято все классы разделать на 2 категории, value objects, immutable объекты и entities, которые mutable.

1) В скале выведение типов отвалилось только в параметрах конструкторов и методов. В любом случае их бы указывали явно для документирования, как сейчас указывают тип возвращаемого значения даже если он выводится.
«ужасные? extends и? super из Java» заменены на ковариантные и контравариантные типовые параметры. Они дают всю необходимую гибкость и при этом сохраняют все прелести статической типизации.
Вообще система типов в скале далеко не самая большая проблема скорости компиляции (неявные параметры дают большую фору), а сложной она становится только при написании сложных библиотек вроде коллекций начиная с 2.8 и не усложняет их использование.

>Что вы подразумеваете под поведенческими классами и классами с данными?

Не более чем последний абзац в пункте «Нарушение инкапсуляции», а именно выделение объектов с данными с простым интерфейсом и нескольких объектов с поведением, обрабатывающие эти данные. Вместо перегруженного интерфейса у объектов с данными «на все случаи жизни».
>«ужасные? extends и? super из Java» заменены на ковариантные и контравариантные типовые параметры. Они дают всю необходимую гибкость и при этом сохраняют все прелести статической типизации.
? extends и? super это фактически урезанные existential types, они по своей мощности сильнее in и out параметров.

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

Можно ли писать хороший софт на Java? Можно! Удобно? Многим удобно.
Берем ООП + immutable + ФП + некоторые другие вкусности и получаем C# ;)
И вы правы, но все зависит от того что считать «другими вкусностями».

Начнем с immutable: var в C# есть, а вот val как-то не добавили. Всегда можно писать static, но надоедает.
Так же immutable и ФП тесно связаны с рекурсивными функциями, а в C# нет возможности контролировать преобразуется ли рекурсия к хвостовой.

Так же среди вкусностей в C# нет кортежей (не очень-то и нужны, но out параметры с immutable не дружат), нет pattern matching, нет встроенного механизма акторов, нет call by name, нет удобной записи для указания цункции в качестве типа параметра или возвращаемого значения, нет упрощенного синтаксиса для частичного применения, нет механизма, упрощающего карирование.

Но все это не отменяет того факта, что scala под CLR не актуальна, а Kotlin даже не пробовал ввязаться. У C# разработчиков есть уже довольно многое, и разработчики языка не дремлют: в последнем релизе добавили ковариантность и dynamic.
И вы тоже правы. Но нектороые вещи из перечисленых вами всетаки есть.
Теже кортежы (Tuple), Func — вполне удобно итд., но не буду вдаваться в детали.

С основной вашей мыслью я полностью согласен — и ФП и AOP и другие парадигмы ни в коем случае не отменяют ООП. И вполне себе используются вместе.
Посыпаю голову пеплом.
Программирую на C# и не знаю о System.Tuple. Оправданием мне может служить только то, что появились они только в 4.0 и использовать кортежи без pattern matching не удобно.
C# замечательный язык. Но, не все удобно в области ФП:
new Dictionary<string, Func<string, string, Expression<Func<MyEntity, bool>>>>
это из реального проекта.
image
Функции как first-class citizen. Да, удобное и компактное представление для паттерна «стратегия»


Исторически было ровно наоборот — функции как first-class citizen появились ещё в 1955 году, а паттерн «стратегия» придмали лет на 30 позже, именно для представления функции как объекта первого класса.

Вообще, лучше попытайтесь выбросить из головы все эти «дизайн-паттерны» — что немцу (швейцарцу Эриху Гамме (вслед за Иммануилом Кантом)) здорово, то русскому смерть. Вообще немецкоязычные люди, по моей практике знакомства с их бизнес-проектами в области ИТ, обладают замечательным свойством за счёт своей дисциплины долго поддерживать проекты с сильно запутанной или давно устаревшей архитектурой. «Дизайн-паттерны» — это разговор на изачально сильно изращённом языке, о чём ещё в 1996, сразу после «классической» книги, написал Питер Норвиг и в 2002 — Пол Грэхэм, с истоками происхождения этого языка.

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

Но полностью избежать изменяемого состояния нельзя, но я стараюсь на сколько это возможно редуцировать изменяемое состояние.
Что изменяемые, что неизменяемые состояния — лишь абстракция. Всё изменяемо. В тоже время, объектов тоже не существует. Спор и головная боль крутятся вокруг абстракций, и это более чем удивительно.
Это как раз неудивительно. Что толку спорить над объективной реальностью ( данной нам в ощущениях :) )? А вот абстракций над ней можно наворотить много и разных.
Да. Т.е. у вас есть желание иметь объект, шаренный между разными потоками и смотреть, вылавливать какой поток в какой момент изменит его состояние, что приведет к крешу? Иметь stateful service(тогда когда это нафиг не надо), который время от времени крешится потому что потоки что-то не поделили? Отлавливать dead lock'и на продакшне? У меня такого желания нет. Я видел много разного говна. Большая часть трудноотлавливаемого говна, что я видел была связана с имзеняемым состоянием и многопоточностью. Так что философия в данном вопросе насчет абстракций и изменяемых состояний, на мой взгляд, немного не к месту.
Я не знаю каким местом думает тот человек, расшаривая объект между разными потоками, не позаботившись о разделении ресурсов. Учитывая, что вы видели много разного говна, думаю не трудно догадаться, про какое место идёт речь.

Приходилось бороться с dead lock-ами на продакшене, и причиной всегда служил не язык программирования или среда, а отсутствие внимательности или мозгов проектировщика/программиста. Не надо перекладывать проблемы с больной головы. С таким же успехом можно заявить, что вообще весь мир не совершенен, зато вот в моём придуманном мире всё тип-топ.

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

Если требуется написать высоконагруженное приложение с обязательным распаралеливанием процессов, это можно одинаково эффективно сделать что на Haskell, что на .NET (C#, F# в связке). Не надо морочить людям голову. Особенно молодым и неопытным.
Я и не говорю, что ООП — это плохо/хорошо.

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

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

При тупом использовании ФП везде и всюду (если конктрено, то в узких местах) лучше жить не становится, а скорее даже хуже (говорю конкретно про Java/Scala).

Так же как и при тупом использовании ООП там где оно нафиг не нужно.

Автомагически можно получить себе только перформанс проблемы, долгие вечера дебугированя и раннюю потерю волос.
> У меня появляется чувство, что объекты это то, что мешает нам писать лапидарный, структурированный и повторно используемый код.

Использование одной парадигмы для всех задач — то, что мешает нам писать лапидарный, структурированный и повторно используемый код.
ООП нужно для моделирования жизни.
Для математики оно, очевидно, не нужно.
Для моделирования жизни хватит СУБД.
Со своей колокольни C(всякие постфиксы) программиста, поглядываю в сторону функционального программирования. И как-то у меня не складывается пазл, вот если мне допустим нужно реализовать некую структуру данных. Я понимаю, что многое конечно уже есть в функциональных языка, и да сях уже никто это сам не пишет. Но… эфективная реализация возможна? Ведь данные неизменяемы, а значит нужно копировать весь список, словарь и т.п. вещи?!
С точки зрения памяти менее эффективно. Но зато неизменяемость даёт доступ к распараллеливаемости, ну и в целом снижает вероятность ошибки.
Да, про достоинства я в курсе, согласен — это большой плюс.
Правильно я понимаю, что допустим все простые операции со структурами данных стоимостью O(1) в императивных языках, неизбежно превращаются в O(n)?
Нет. У большинства структур данных существуют методы сделать их версионными.
В отсутствии gc они обычно крайне сложны, но тем не менее.
Показалось что разобрался, но потом опять запутался. Хорошо, пусть есть оптимизации. И мне нужно реализовать односвязный список, с возможностью вставить новый элемент в середину. Вот вставляем элемент, в нем указатель на следующий, нужно поменять указатель у предыдущего. Меняем, а так данные неизменяемы, нужно поменять указатель у предыдущего предыдущего и так далее. Да, из-за оптимизации копироваться объекты вроде не должны, но в своей реализации я должен дойти до начала списка, как-бы создавая новые объекты, чтобы все было по честному. Да, получается без изменений, но это лишний проход?
Вариант предложенный вами — один из возможных. С одним исключением.
Вы это можете делать УЖЕ пока идете в то место, где хотите вставить элемент. Вам так или иначе все равно нужно это все пройти(список то односвязный).
Потому проход один.
Наверное выход, правда это не хвостовая рекурсия, и это «тянет» стек.
А общей оптимизации для таких случаев нет? К примеру, список может быть двухсвязным, как с ним быть?
Копировать _всё_ не нужно. Известно что данные на которые ссылается список не меняются, значит нужно скопировать измененный список указателей (с точки зрения реализации). А фактически, если операции производятся с началом списка, то изменяется только один элемент. Чтобы поменять в дереве ветку на глубине n, нужно создать n нодов составляющих новое дерево, все остальные неизменны и можно использовать указатели на них в реализации языка. Так что не всё так плохо.
Здесь я задал вопрос. Вам я его тоже хочу адресовать, прокомментируйте пожалуйста.
Например в хаскеле копирование неизменяемых вещей сильно оптимизировано.

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

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

Реализации намного эффективнее чем можно представить. Сравните бенчмарки бинарников(!), генерируемых ghc с java, python, c. Будете удивлены.
Запятой не хватает:

Сравните бенчмарки бинарников, генерируемых ghc, с производительностью программ на java, python, c. Будете удивлены.
Это проясняет, спасибо. Интересно, где можно почитать, детали реализации в .net или jvm? Не уверен насчет jvm, в .net GC и в VM нет никаких оптимизаций для этого, и это настораживает. Не потянет ли это за собой увеличение нагрузки на GC? Например, если в процессе активно создаются объекты.
Может есть книги по этой теме? Что нить вроде Рихтера.
Посмотрите с высоты колокольни Java-программиста…
НЛО прилетело и опубликовало эту надпись здесь
Синглтон реализованный например через статический метод возвращающий инстанс проблематично смокать (powermock конечно решает проблему, но проблема существует тем не менее).
Синглетоны мокаются на ура. Заводим в классе контейнере, в добавок к getInstance setInstance и передаем туда мок. Никаких повермоков не надо.
Ну я ж говорю, зависит как написать. Но если синглтон из другой библиотеки? Городить контейнеры для чужих синглтонов? Опять лишние воркэраунды не связанные с задачей.
И в интерфейсе будет ненужный для приложения метод?
Ну и ничего страшного :) Довольно трудно сделать приложение тестируемым, не вставляя подпорки для него в код.
Вы не поверите, в идиоматическом Clojure 99% кода тестируемо без «подпорок» для него в коде. :)
Ну конечно тестируемо, язык ведь динамический и все кишки торчат наружу. Я имею ввиду классическое ООП, а не маргинальный язык.
Как только мы добавляем setInstance, так Singletone перестает быть Singleton`ом.
НЛО прилетело и опубликовало эту надпись здесь
И сразу получаем проблему независимости тестов. Определяем TestInitialize и TestCleanup. Получаем проблемму с конфигурирование отдельных тестов. Определяем класс очистки и установки среды через IDisposable с параметрами в конструкторе — получаем сложную систему подготовки среды к тестирования.

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

Решение:
Передаем объект от GetInstance через параметр (конструктора или метода) и забываем о всех проблеммах раз и на всегда. По сути отказываемся от сингелтона.
Вообще лучше вместо синглетонов использовать dependency injection, но не все пишут проект с нуля, так что это вполне нормальная необходимость.

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

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

Про синглетонность — её нынче трендово оставлять на откуп dependency injector'а, что, думаю, именно «в точку».
Боюсь показаться кэпом, но все же есть области, где лучше использовать ООП, и есть области, где лучше использовать ФП, а нам, программистам, следует просто использовать верные инструменты, сохраняя лаконичность, читабельность и повторную используемость кода.
Довольно однобоко. Как, впрочем, и утверждение «ООП всегда лучше». Зависит от задачи, от умения реализоввать и т.д. Конечно, если взять шаблоны проектирования и поставить себе цель «натягивать» их на любую задачу — выйдет кака. Но это не говорит о том, что ООП плохо, это говорит о том, что инструмент довольно тонкий, и надо применять очень с умом.
Этот чувак отказывается от ООП в пользу Scala и Clojure. Это же оксюморон. Scala и Clojure объектно-ориентированы до мозга костей. Что отличает их от кондового ООП Java, так это смещение фокуса на изменяемые/расширяемые классы с неизменяемыми внутренностями.
А в чём проявляется объектно-ориентированность clojure до мозга костей? На clojure для многих задач можно обойтись только map, vector, set. При этом не требуется создание каких-либо новых классов.
Сущности, которыми оперируют map, vector и set определены как типы данных реализующие определенные интерфейсы и протоколы. Более того, интерфейсы и протоколы в Clojure важнее чем, скажем, функции — через них выражена семантика языка. Если тип данных реализует протокол функции, то экземпляры этого типа и будут функциями. В этом смысле Clojure даже более объектно-ориентирована чем Java.
Да, т.к. clojure хостится на jvm, то всё, чем он оперирует, в том числе и функции — объекты, реализующие определённые интерфейсы. Но при написании програм об этом не задумываешься, это скрыто от программиста. Если писать какие-то свои специфические типы данных, то возможно над этим придуматься задуматься. Я не писал большие промышленные приложения на clojure и не знаю, насколько это верно там. Но в своих маленьких приложениях мне редко приходилось возвращаться к ООП, если только не требовалось взаимодействовать с java библиотеками.
Практически во всех основных библиотеках типа clojure.core, clojure.logic и т.п. протоколы используются явно и в больших количествах, и как правило не для взаимодействия с Java-библиотеками. В особенности это относится к не-jvm реализациям Clojure, например ClojureScript.
Так одно дело использовать в библиотеке, тем более в таких крупных, другое дело в своём собственном коде, от которого не требуется абстрактность. Многие крупные java библиотеки, например spring используют тонны reflection'ов, а иногда и манипуляцию с байткодом, но это не значит, что в джаве это повседневная практика. А в вашей практике вы часто использовали протоколы?
Протоколы это один из основных механизмов абстракции в (современной) Clojure, а не нечто эзотерическое типа рефлекции и манипуляции байткодом. Загляните в исходники библиотек — они очень малы (по сравнению с джавовскими) и там нет ничего сверхестественного.

В последнее время я писал на С#, а когда писал на Clojure протоколы только-только появились. Так что нет, я не часто использовал протоколы.
Объектно-ориентированная парадигма всё более (с 1994 года — с появления пресловутого труда про дизайн-паттерны, по крайней мере) развивается в сторону пародирования функциональной (дизайн-паттерны, кроме очевидных случаев, и есть перенос приёмов функционального программирования в ООП через использование объекта класса в первую очередь как носителя «главного» метода этого класса).

A Java — в общем, практически готовый функциональный язык с точностью до syntactic sugar (который в Scala и встроили).
А мне вмё чаще кажется, что мы находимся на краю познаваемого, потому, что частота выстрелов принципа неполноты Гёделя составляет несколько раз за проект…
Выходом является смена порочной парадигмы, а её приходится менять часто.
А вы не могли бы более подробно рассказать, как выстреливает принцип неполноты Геделя в обычных бизнес проектах?
Спасибо за перевод.

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

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

Хотя бывают исключения. Но всё зависит от задачи и её объема.
Вот статья на хабре фактически об этом же, однако заминусованная по самое ой-ой-ой habrahabr.ru/post/140511/ И где справедливость?
Жаль уже минусовать нельзя…
По ссылке 8 строчек ни о чем…
Собственно вот причина минусов.
Так что все справедливо.
Имхо, очень интересная, нестандартная и заслуживающая внимания реализация ООП в Эрланге.
Там нет объектов как таковых, там есть актёры, которые обмениваются сообщениями и логика которых пишется на в функциональном стиле. Но это их взаимодействие уже не является функциональным, оно скорее ООП.
В результате получается, что объекты не плодятся для всего подряд, а только по делу.
НЛО прилетело и опубликовало эту надпись здесь
нужно написать статью — ужас объектно ориентированного программирования. Или — об ужасах объектноориентированого программирования. Я вот тоже от него хочу отойти но не получается сделать это там где оно навязывается. Скажем так — ООП избыточно сложное программирование. Ужас и кошмар. Долой его!
Я вот тоже от него хочу отойти но не получается сделать это там где оно навязывается.

Что мешает?

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

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

Но в Долине, например, смена работы раз в год это норма. Так, выбором компании, можно косвенно проголосовать за парадигму или за определённый ЯП.

Я бы поддержал своей работой компанию, занимающуюся разработкой на Haskell. Но, пока приходится просто не программировать =)
еще момент — я сейчас пишу код который привязан к .Net ООП ориентированной библиотеке. И ничего с этим не поделать. Посему приходится наследовать от него объекты. Вот и не могу выйти из этой парадигмы. Я вообще считаю что сложность есть категория восприятия и программирование должно максимально удалять эту категорию. Вполне возможно что можно все представить все просто. А вот ООП как раз и увеличивает сложность программы. Делает ее более сложной — т е идет в обратном направлении. => ООП чистое ЗЛО.
Возможно, не всё так плохо.

Большинство разработчиков Haskell работают в Microsoft Research.

И всё функциональное, что можно прикрутить к .Net, они прикручивают в языке F#. Не имею дел с .Net, но мне кажется что F# не такая уж плохая штука.
OK, вы почти доросли до Ruby.
> Бизнес компоненты нельзя использовать повторно

> Вскоре стало понятно, что возможность создавать повторно используемые бизнес
> компоненты это заблуждение. Каждый бизнес отличается от всех остальных, даже в одной отрасли
Интересно, почему речь идёт о бизнес компонентах, а не о просто компонентах.
Возьмём, например, календарь/date-picker, или например color picker. Повторное использование такого рода компонентов без каких-либо модификаций в рамках одного проекта вроде бы вопросов не вызывает.
В рамках нескольких проектов — допустим по разному выглядящих приложений — MVC паттерн для того придуман, чтоб отделить view и позволить его корректировать отдельно.
Задача решается отлично, так что, неужели ООП действительно так плох?

> Некоторые паттерны стали даже анти-паттернами (например, паттерн singleton приводит к невозможности создания модульного тестирования).
Это неверно — singleton совместно с dependency injection контейнером например вполне позволяет заменять реализацию версией для тестирования, и антипаттерном не является.
Вообще это сильная натяжка, ибо наличие парочки неудачный паттернов, записанных со временем в «антипаттерны», вовсе не означает, что все остальные паттерны — тоже кандидаты в антипаттерны.

> Фреймворки классов и компонентов не очень эффективны для повторного использования
См. выше про календарь и color picker.

> Каждый подкласс немного отличается от своих родителей. Что ведёт к множеству изменений в дочернем классе.
> Или к попыткам сделать базовый класс более общим. В конечном итоге мы получаем хрупкий код
Сложно понять о чём идёт речь в целом, и в частности чем плохи «попытки» сделать базовый класс более общим.
Сюрприз еще в том, что замены в виде mixin и traits, которые автор пропагандирует в тексте далее, это тоже ООП.
Странно критиковать одни вещи в ООП, восхвалять другие, и называть это суммарно «критикой ООП».

> Например, нелепо просить объект отрендерить себя в HTML
В общем описании получается действительно нелепая задача — зачем, например, просить объект класса DocumentBuilder — т.е. XML parser — отрендерить себя в HTML? Что вообще требовалось достичь?
В теж же случаях, когда в задаче есть смысл — например мы хотим отобразить в HTML тот же календарь — мы опять вспоминаем об MVC.

> Изменяемое состояние — причина проблем
Изменяемое состояние, видимо, появилось впервые в ООП (-:
Не хочется даже комментировать об immutable objects и прочих очевидных вещах, хочется только сказать что к ООП это имеет мало отношение. Скорее это хвальба ФП, где контроль над состоянием попытались максимально скрыть/отобрать у программист (ибо он, тупица такая, не способен не налажать ((-: ). Если вы об этом хотели поговорить, напишите статью Императивное vs. Декларативное программирование, но не перекладывайте всю вину на ООП.

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

> Изменяемое состояние, видимо, появилось впервые в ООП (-:

Но именно ООП пытается скрыть это состояние. Оно не просто пытается бороться с этими побочными эффектами, оно предлагает просто прикрыть эти побочные эффекты, чтобы было «незаметно».

> В общем описании получается действительно нелепая задача — зачем, например, просить объект класса DocumentBuilder — т.е. XML parser — отрендерить себя в HTML? Что вообще требовалось достичь?
В теж же случаях, когда в задаче есть смысл — например мы хотим отобразить в HTML тот же календарь — мы опять вспоминаем об MVC.

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

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

Ещё общая мысль, не связанная с сабжем. Зачем объединять данные и функции в классы и объекты? Никогда этого не понимал и не понимаю до сих пор. Просто набор структур и обрабатывающие их функции прекрасно себя чувствуют просто сгруппированными вместе в одном модуле. Или даже не в одном модуле. Почему-то не видел чтобы этот простой вопрос где-то поднимался. Новичкам в программировании сразу предлагают руководства в стиле «ООП это круто. Итак, ООП это ...». Вопрос «почему круто?» остаётся без ответа и потихоньку забывается к тому моменту, когда программист добирается до template <class AtomicType, template class Unit> class GenScatterHierarchy: public Unit…
> Но именно ООП пытается скрыть это состояние. Оно не просто пытается бороться с этими побочными
> эффектами, оно предлагает просто прикрыть эти побочные эффекты, чтобы было «незаметно».
ИМХО, скрыть состояние пытается ФП а не ООП. ООП только разделяет его на части, структурирует. Но не скрывает. А вот ФП да — скрывает, и делает вид, что состояния нет.

> Делать методы render() для всех объектов как-то неправильно
Неправильно. Как уже было вспомнено, есть MVC. Каждый класс должен иметь свой view класс. Он может рендерить часть DOM дерева, и его можно передавать в этот самый Render абсолютно аналогично, и будет у вас Система Рендеринга как контроллер, объекты/классы как модель, и renderer class для каждого класса из модели как view.
> ИМХО, скрыть состояние пытается ФП а не ООП. ООП только разделяет его на части, структурирует. Но не скрывает. А вот ФП да — скрывает, и делает вид, что состояния нет.

Как раз ФП кардинально от него избавляется везде, где это возможно. А где нельзя делает состояние более явным.

Про MVC ничего не могу сказать. Пожалел я своё время чтобы разобраться с этой штукой. Поэтому интерфейс рендерил библиотекой на MVC, а вот прикладную задачу решал по-простому. Рендерил рабочий экран САПР с железками, динамическими размерами и прочими объектами.
> Как раз ФП кардинально от него избавляется везде, где это возможно
Под всеми ЯП есть процессор с регистрами, оперативная память и т.п. — избавится от них не представляется возможным и даже целесообразным. ФЯП какраз скрывают от вас это состояние, но не избавляются совсем от него, ибо это невозможно. ИМО
Но так или иначе, что ООП, что ФП — все имеют свои преимущества и недостатки. Если вам больше по нраву ФП, это еще не значит что ООП — ненужный мусор.
«Общая мысль» ваша совершенно непонятна.
Вам никогда не рассказывали на лекциях там, или не попадалось в текстах упоминаний о том, какая идея стоит за ООП? О том, что люди в общем и целом понимают мир как набор объектов с определёнными свойствами и поведением, и объекты эти можно разделить на классы и подклассы. Например Автомобиль — это класс объектов, абстрактный. Легковой Автомобиль — некоторая конкретизация класса Автомобиль (все Легковые Автомобили — тоже Автомобили), но всё ещё абстрактная сущность. А вот машина соседа Дяди Васи — это объект вполне конкретный, который относится к классам Автомобиль, Легковой Автомобиль и т.п.
Таким образом мы имеем, я бы сказал, «универсальный DSL на все домены», простой и интуитивно понятный.

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

Я думаю это плохо если в основе технической дисциплины лежат философские рассуждения.

Я не разделяю их, но это уже не существенно.

Может меня и окружают «объекты». Но моя кружка с чаем не может питься сама. И дверь не может открываться сама. И справочник не может искать сам в себе.

И кружка у меня в уме не наследуется от «общего класса посуда». Скорее зависимости от ситуации она обладает свойствами посуды, керамического изделия, хрупкого предмета (type classes).
В основе любой технической дисциплины лежат философские рассуждения если смотреть глобально. Технические дисциплины формируют правила, способы, методы, законы, свойства и т. п., а философия вводит эти понятия.

Если кружка пьётся сама, то это плохая модель. Пить должен юзер, user.drink(cup);, а не cup.drink_by(user);

У меня в уме кружка унаследована (имеет свойства и методы) от посуды (и через иерархию вообще от физического объекта) и агрегирует свойства своих материалов и деталей, в том числе керамичность и хрупкость.
Всё идет к
drinkManager.perform(user, cup)
, а
user.drink(cup);
и
cup.drink_by(user);
, если уже по глупости были сделаны, для обратной совместимости подцепляются через «визитор».
Есть такая тенденция, но обычно перехожу к менеджерам только когда возникает (или предвижу скорое возникновение) необходимость в одинаковом действии субъектов, которых сложно внести в одну иерархию. Скажем, умели изначально пить только люди, но внезапано заказчик вводит в предметную область домашних животных, которые в рамках предметной области имеют только это общее свойство.
Вы привели неудачный пример с кружкой. Очевидно, что «кружка» — это пассивный объект из нашего окружения. А вот электрочайник — уже активный. Он может «кипятить воду», если его «попросить» об этом. И как представить операцию «кипятить воду» отдельно от объекта «чайник» в данном случае?

Дело в том, что ООП — это просто один из способов декомпозиции задачи для того, чтобы её можно было решить. Задачу можно декомпозировать по-разному. Более того, разные части задачи можно декомпозировать с помощью разных подходов. По этому говорить, что один подход принципиально лучше другого — это ошибка. Так можно утверждать только в контексте конкретной задачи. Понятно, что ООП — это не серебряная пуля. Просто потому, что серебряных пуль не бывает. И странно, что автор оригинальной статьи (с 17-летним опытом!) этого ещё не понял. Или сделал вид, что не понял. :)

Но если создавать класс «система рендеринга», то на вход ему нужно передавать все поля отрисовываемых объектов. Либо эти объекты целиком, но давать системе рендеринга доступ ко всем полям этих объектов. Оба этих способа оставляют ощущение «неправильности».

Варианты:
— передавать «фэйковый» объект/структуру (модель представления), содержащие только необходимые для рендеринга данные из объекта модели. Это может быть как «тупая» структура данных, содержащая клонированные данные непосредственно, так и обёртка/адаптер/прокси, транслирующие необходимые свойства объекта модели на свои свойства в одностороннем режиме (причем не обязательно один к одному), причём структура это или обёртка (или их комбинация, например использование ленивого клонирования) для системы рендеринга всё равно, эти детали инкапсулированы.
— реализовать в объекте модели интерфейс, позволяющий получить доступ только к необходимым данным в режиме read only
— …

Зачем объединять данные и функции в классы и объекты?

DRY и KISS?

a = 1;
b = 2;
c = 3;

p = get_triangle_perimeter(a, b, c);
s = get_triangle_square(a, b, c);

, да даже
triangle = new Triangle(1, 2, 3);

p = get_triangle_perimeter(triangle);
s = get_triangle_square(triangle);

против
triangle = new Triangle(1, 2, 3);

p = triangle.getPerimeter();
s = triangle.getSquare();


Или даже не в одном модуле.

Данные становятся глобальными?

Второй и третий пример эквивалентны. Я бы написал примерно как во втором примере, чтобы Triangle было простой структурой. Именно из-за KISS.

Прописывать функции именно как методы классов не вижу никакого смысла. Просто сама структура передаётся в функцию не как параметр а как self или this.

> Данные становятся глобальными?
Типы данных и функции. Не то чтобы уж сильно глобальными, область видимости ограничивается модулями, неймспейсами и т.п.
Эквивалентны они в плане генерируемого байт-кода. Но семантически третий пример, по-моему, проще — нет передачи параметра или определения контекста действия функции, в конструкции triangle.getPerimeter triangle выступает и параметром, и «нэймспейсом». Во втором примере мы умножаем сущности. Тут это ещё не так очевидно, но когда у нас будут не только треугольники, но и круги, квадраты, прямоугольники и т. п. нам нужно будет писать функции get_rectangle_perimeter и т. д., а для использования в одном контексте разных фигур как-то самим реализовывать полиморфизм, например
get_perimeter(figure) {
  if (figure instanceof Triangle)
    return figure.a+figure.b+figure.c;
  else if (figure instanceof Rectangle)
    return (figure.a+figure.b)*2;
}

Это тоже просто? А ООП языках полиморфизм средство языка. Компилятор или рантайм решают какую реализацию функции get_perimуter(figure) выбрать.

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

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


А причём тут MVC? MVC не ООП парадигма.
Тем не менее в ООП отлично работает, и решает те задачи, которые по словам автора статьи, с помощью ООП не решаются.
Тут согласен, я скорее приверженец ООП, чем чисто процедурной парадигмы :)
Вообще это сильная натяжка, ибо наличие парочки неудачный паттернов, записанных со временем в «антипаттерны», вовсе не означает, что все остальные паттерны — тоже кандидаты в антипаттерны.
«Ви, возможьно, бедете сьмеятьься», но все паттерны проектирования основаны на таком анти-паттерне, как «копи-паст».
Таки буду.
Еще скажите что все операторы основаны на таком «антипаттерне». Ведь присванивание, проверка на равность/неравность и т.п. — они же «копи-пейстятся» повсюду. Ужас ужас прямо.
В статье одна вода и никакой конкретики! Такое ощущение, что автор просто не смог подружиться с ООП за все 17 лет и теперь ведет его антипропаганду.
После вот этого: «например, паттерн singleton приводит к невозможности создания модульного тестирования» стало понятно, что дальше правды тоже ожидать не стоит. Утверждение-то бездоказательно (а вот обратное доказывается элементарно, достаточно показать один пример модульно тестируемого синглтона… надо?)
Так проблема не в тестирование синглтона, а в тестирование кода, который зависит от синглтона.
Ээээ… а в чем проблема? Тот факт, что синглтон по определению один в приложении, еще не означает, что его нельзя подменить. Это все исключительно вопрос реализации.
Это означает, что в стандартных реализациях, его сложно подменить. Либо его передавать везде, что не очень удобно, либо использовать dependency injection, что хорошая альтернатива, но не все его используют.
Это проблема не паттерна, а стандартной реализации, которая была придумана в те времена, когда unit testing не был в активном использовании. Все меняется.

Но при этом сам паттерн ни в чем не виноват, он прекрасно работает.
Зачастую его легко подменить на этапе импорта в тестовое окружение.
С синглтоном та же проблема, что и с любыми глобальными данными: невозможно сказать какие синглтоны использует метод, не прочитав его и все вызываемые им методы.

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

Например в синглтоны очень любят оборачивать обращения к БД. В итоге любой код может обращаться к БД.

Я понимаю, что хорошие программисты и с синглтонами пишут программы так, что их потом просто тестировать. Но таких хороших программистов не хватает. По этому паттерны оцениваются не только по пользе в руках опытного программиста, но и по потенцеальному вреду в руках неопытного.
«С синглтоном та же проблема, что и с любыми глобальными данными: невозможно сказать какие синглтоны использует метод, не прочитав его и все вызываемые им методы.»
Каким это образом? Тот факт, что синглтон уникален, не означает, что его надо использовать как глобальные данные.

«Например в синглтоны очень любят оборачивать обращения к БД. В итоге любой код может обращаться к БД»
Просто надо видимость ограничивать, вот и все.

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

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

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

Публикации

Истории