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

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

Как обычно, куча помоев вылитая на одну парадигму и уже заезженное "Пикачу ФП я выбираю тебя". Без агруменов, примеров, и какого либо описания того, как же это круто и почему это круто.
Невротики безустанно пытаются оправдать свой выбор марки автомобиля, сигарет, смартфона или парадигмы программирования перед окружающими.
Ну, это мне сейчас веганов напомнило.

Я — функциональщик. Прекрасно себя чувствую. Это вы от объектов такой раздражительный.
А что функциональщики у нас не используют объекты и прочие чуждые для них элементы?
«Новообращённые святее Папы Римского» ©
Помнится недавно в обсуждении ФП, один из его адептов отказывался признавать LISP в качестве ФП (язык с которого ФП и началось), за то что в новых его версиях была добавлена поддержка ООП.
НЛО прилетело и опубликовало эту надпись здесь
LISP появился раньше, чем C++ и последующая мода на ООП. LISP появился в 1954 году, C++ в 1983 году, а первая версия Object LISP в 1985 году. Однако, мода на ООП всех захлестнула после Windows 3.0, так как под DOS и UNIX без проблем обходились и без ООП, от чего был былинный срач о ненужности/нужности ООП, а вот Windows уже требовал именно ООП чтобы писать что-то неконсольное.

PS поскольку речь зашла о связи Windows и ООП, то интересно как работают без ООП интерфейсы у ФП программ написанных без ООП?
поскольку речь зашла о связи Windows и ООП, то интересно как работают без ООП интерфейсы у ФП программ написанных без ООП?
Я про пользовательский интерфейс программы.
НЛО прилетело и опубликовало эту надпись здесь
Спасибо!
а вот Windows уже требовал именно ООП чтобы писать что-то неконсольное

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

В JS невозможно реализовать сколь-нибудь полезную чистую функцию. О каком ФП тут можно говорить?

Поясните подробней пожалуйста с чем это связано?

Как чистые функции связаны с фп?

Невозможность реализации полезной чистой функции.

подозреваю что он о том, что сторонний код может что-то используемое в функции ВНЕЗАПНО подменить(например undefined)

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

НЛО прилетело и опубликовало эту надпись здесь
Это статья детектор. Для выявления тех, кто не знает ООП или очень мало (лабки в вузе) писал на нем. Я полностью согласен с автором. Поддерживать ООП-код, когда у тебя 100500 классов и чтобы добавить хоть какую-то доп-фичу нужно перелопачивать 100500 классов и интерфейсов, это ад. Вы видели Java Framework'и? Как вам? Ужас? А что творится с кешем, а виртуальное наследование? Это сущий кошмар.

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

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

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

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

Например?


И когда архитектура разрастается, то поддерживать и расширять её становится настолько сложно, что она превращается в «плохую архитектуру».

Так, а альтернативные предложения?


Давайте вы сами (да и отписавшиеся здесь) не будете себя обманывать. Вы никогда не писали архитектуру.

Оу да, конечно, вам виднее, что я (и остальные отписавшиеся) писал.

Если вы писали полиморфизм и парочку extends-implements, то вы не писали архитектуру. Давайте не будем забывать, что 99% здесь отписавшихся никогда не испытывали проблем с кешем и производительностью из-за использования ООП. И 99% здесь пишут ООП ради ООП.

Я же говорю, вам явно виднее, кто что писал. Зато как конкретные вопросы — так никаких ответов. Мимими.

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

Ну и да, признаюсь, я правда никогда не испытывал проблем с кэшом и прозводительностью из-за использования ООП. Разное было, и хреновый I/O (пятикратная просадка), и идиотская ошибка в распаковке base64 (шестидесятикратная просадка), и всякие другие вещи — а вот ООП как-то не мешало пока еще.

> никогда не испытывали проблем с кешем и производительностью из-за использования ООП

мм… А вы вообще в курсе, что ООП-программы компилируются в тот же ассемблер, что и всё остальное?
У него свой ассемблер с блекджеком и ш@#$%^и)) И в его ассемблере ООП при компиляции вызывает проблемы с кешем и производительностью)) Ну чего вы набросились на человека?)))
Ну справедливости ради — проблемы с производительностью у ООП вполне себе реальная тема. типичный сценарий — быстро прототипируешь логику на простых SOLIDных классах.
Десяток уровней абстракции, сотня классов в предметной области где высокая связность всего и вся. И получаем дикую потерю в скорости. Но!
1 — без ООП мы бы еще схемы на доске рисовали, а не проблемы щупали.
2 — Задача обычно решается так или иначе кешированием, или некоторым нарушением изящной структуры.
3 — даже если задача действительно плохо пригодна для эффективной работы в ООП-парадигме (например в конечном итоге у нас оказывается 80% пятиэтажных join «в узких местах», которые занимают 80%, то всё равно гораздо проще и эффективнее писать это всё поверх уже работающей пусть и неэффективной структуры, чем «в вакууме».
4 — реально задач таких почти и нет. В голову сейчас приходит только один пример, да и то не у меня был — там предметная область была — научные исследования в области генетики. «Божественный код» это классическая лапша, поскольку о структуре там никто не думал, так что слабо детерминируется, и анализ его аналогично вызывает вопросы. Тут нельзя отрефакторить предметную область :)

Резюмирую — ООП действительно имеет проблему с производительностью, но это цена за более быструю и понятную разработку. К тому-же как правило в прямых руках цена незначительна.
Десятки сотен уровней абстракции… Сотни тысяч классов… Пятиэтажные json… Вы упретесь в невозможность поддержки такой системы намного раньше проблем с производительностью, независимо от того что у вас под капотом — ФП, ООП или гибрид!!!

Ну включите уже наконец моск! Люди неспроста придумали микросервисы, сервисные шины и оркестровку!!! Все кто солидарен со статьей здесь говорят ООП подразумевая, что это определяет архитектуру системы… да как так?! Вы сколько лет в этом бизнесе народ?!

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

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

И мы опять возвращаемся к тому же самому. Что ООП != архитектура. Вы повторяете ту же ошибку, что и lookid и JustMoose выше. Делаете однозначные заявление о технологии в принципе, не учитывая детали ее применения.

Класс который связан с тяжелыми вычислениями, если один-единственный на микросервис или же вплетен в иерархию классов монолитного приложения. Как оценить его производительность абстрагируясь от архитектуры, в которой он живет? От методов, в которых он используется?
Ничего что это вы именно тот человек который предлагает технологию способную отстрелить ногу не ориентируясь на контекст?) Сложность системы не является показанием для микросервисов и прочих решений. Зависит от множества факторов.
А что тогда является показанием для архитектурных решений Карл?! о_О
Да, поскольку часто выделение микросервов сокращаит оверхед. Например если части запроса асинхронные, не?

И вообще, мы как то ушли от основной темы. В общем я к чему — в целом вообще неважно ООП у тебя или ФП. Накосячить можно и так, и так.
Да, поскольку часто выделение микросервов сокращаит оверхед.

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


в целом вообще неважно ООП у тебя или ФП. Накосячить можно и так, и так.

Можно подумать, с этим кто-то спорил.

Голословное утверждение

Что именно голословное утверждение? Что распределенная система добавляет оверхед?


повышение или снижение производительности напрямую зависит от задачи

Не от задачи, а от архитектуры и контекста.

Нее. Там бы всё и утонуло под весом «микро»сервисов)
Вы тут совсем не в тему сказали.
Если точнее, то в команды процессора или байт-код, для java например.
НЛО прилетело и опубликовало эту надпись здесь
Не хотите написать статью про дырки в ФП?
Вопрос не ко мне, но думаю, что это к вопросу ленивых вычислений.
Скажем, банальная хвостовая рекурсия в Haskell спокойно выкушает всю память.

Но я не считаю, что это проблема — просто надо понимать, что пишешь.
НЛО прилетело и опубликовало эту надпись здесь
>Это статья детектор. Для выявления тех, кто не знает ООП или очень мало (лабки в вузе) писал на нем.
Тут согласен. А дальше ересь пошла.
Кхм… Этот комментарий детектор. Для выявления тех, кто не знает ООП или очень мало (лабки в вузе) писал на нем (или тех, кого на нём писать [толком] не научили). Простите, но не смог удержаться… (ибо чистая правда)
Наш проект собирается пол часа. Он достался нам по наследству от коллег, которые раньше писали с применением ФП, потом сели на C++ Builder и поехали всякие TХреньКотораяДелает() --> TХреньКотораяДелаетБольше() --> TХреньКотораяДелаетМного(). Так же пошли структуры с указателями на классы для передачи данных между ними… и т.д. и т.п.

Пытаемся по немногу рефакторить сего монстра. Встаивание функционала по ТЗ (т.н. «улучшения») в код, построенный на ФП происходит раза в четыре быстрее, чем в унаследованный код. Львиную долю времени приходится отслеживать связи, чтобы понять причину падения при успешной сборке.

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

Вы правда думаете, что ФП нельзя злоупотребить в такой же мере?

Люди сменили парадигму программирования. Естественно, что проект, на котором они осваивали новую, имеет, мягко говоря, некоторые недостатки. Чувство любого инструмента приходит после периода проб и ошибок.
Просто проблема автора в том, что он не использует парадигму, а поклоняется ей. Когда-то поклонялся ООП, потом нашёл в нём проблемы, и его мир был буквально разрушен. Теперь (с горя наверно) также стал поклоняться ФП. Ждём статью годика через два с заголовком: «Прощай, функциональное программирование».
«Здравствуй процедурное».
«Здравствуй процедурное».


Хм. Нужен ли goto?
Кнут в «Искусстве программирования» не стремался его активно использовать.
Эту ересь уже несколько раз обсуждали в комментариях. :)

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

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

П.С.
ООП использую в меру, без фанатизма.
Это смотря что считать китами.

«Actually I made up the term „object-oriented“, and I can tell you I did not have C++ in mind.» — Alan Kay
Кто вам сказал, что именно это киты ООП?

GoF 20 лет назад разжевали, что наследование использовать не надо, и как именно не надо.

Добавлю статью на тему от Герба Саттера — "Uses and Abuses of Inheritance": часть 1, часть 2

А ссылочка на пожёванное есть?
Гугл даёт вот эту статью: https://habrahabr.ru/post/280960/
Но лучше, наверное, подождать ответа areht-а.
Первая ссылка в гугле

Design Patterns: Elements of Reusable Object-Oriented Software. Erich Gamma, Richard Helm, Ralph Johnson and John Vlissides.
Приёмы объектно-ориентированного проектирования. Паттерны проектирования. Эрих Гамма, Ричард Хелм, Ральф Джонсон, Джон Влиссидс

pdf тоже гуглиться несложно, на разных языках

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


Что любопытно, конкретная реализация интерфейсов например в Java — плохая. Использовать для интерфейсов отдельное ключевое слово implements — плохо. Но это, конечно, не меняет того факта, что поведение со скрытым состоянием в ООП — главное.

Догадаться несложно — поверить тяжело )
Мне жаль, что давным давно я использовал термин «объект» для этой темы, потому что из-за этого многие люди фокусируются на меньшей из идей. Большая идея это «сообщения» Ключ к созданию хороших масштабируемых систем это проработка механизмов общения модулей, а не проработка их внутренних свойств и поведения.

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

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

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

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

Во-первых, это само по себе не правда. Во-вторых, только очень маленькое количество программ линейно.


ей не нужны никакие именования

Это тоже не правда. Во-первых, она сама должна быть поименована, во-вторых, используемые ей операции должны быть поименованы. Это необходимый минимум.


инвалидация кэша не относится к задачам программирования.

Почему это?

> Во-первых, это само по себе не правда. Во-вторых, только очень маленькое количество программ линейно.
Все программы линейны.
> Это тоже не правда. Во-первых, она сама должна быть поименована, во-вторых, используемые ей операции должны быть поименованы. Это необходимый минимум.
Не должна. Не должны.
>Почему это?
Потому что, тезисы должен доказывать тот, кто их выдвигает.
Все программы линейны.

В таком случае тезис "нет никакой сложности в написании линейной программы" ложен. Потому что если все программы линейны, то этот тезис эквивалентен "нет никакой сложности в написании любой программы", а это заведомо не так.


Не должна. Не должны.

То есть используемая вами программа не имеет никакого имени, и используемые в ней операции — тоже не имеют никаких имен?


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

Ну вот и доказывайте тезис о том, что "инвалидация кэша не относится к задачам программирования" — вы же его выдвинули?

> а это заведомо не так.
Только в вашей вселенной. Это ваш тезис.
> То есть используемая вами программа не имеет никакого имени, и используемые в ней операции — тоже не имеют никаких имен?
Именно так. К моей задаче программиста не входит выдумывать никакие названия. Кроме как для целей повторного использования кода.
>Ну вот и доказывайте тезис о том, что «инвалидация кэша не относится к задачам программирования» — вы же его выдвинули?
Нет, вы выдвинули тезис, что это задача программирования. Если вы уже забыли об этом, у вас действительно все плохо.
Только в вашей вселенной. Это ваш тезис.

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


Именно так.

Приведите пример. Я не знаю ни одной программы, не имеющей названия.


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

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

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

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


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

Рекурсивные определения не ко мне.


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

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


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

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

> В таком случае вся индустрия внезапно занимается не тем. Угу.
Не знаю чем занимается ваша выдуманная индустрия, моя занимается проблемой повторного использования.
> Рекурсивные определения не ко мне.
Я заметил, что у вас сложности с ведением диалога.
> Содержащая десяток именованных объектов. Поэтому, внезапно, задача именования все еще актуальна.
Именование этих объектов не относится к программированию никаким боком, если вы не заметили для написания этой безымянной программы (кстати, поздравляю, вы ее увидели первый раз в жизни), мне не пришлось выдумывать ни одного нового наименования.
> Никто не ставил мне эту задачи, они возникли как частная проблема внутри конкретной бизнес-задачи
Ура, теперь и вы поняли, что задача инвалидации кэша является задачей бизнеса, а не программирования, и ключевое решение «какой вариант выбрать» принимается на уровне бизнес-планирования. А вот уже обычную задачу «инвалидация кэша при вот таком-то выбранном бизнес-решении» можно решить и программированием.
Не знаю чем занимается ваша выдуманная индустрия, моя занимается проблемой повторного использования.

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


PS


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

А вот и нет. Бизнесу на это положить, у бизнеса есть ровно одно требование: 30+ транзакций такого типа в секунду (при такой-то загрузке).


Впрочем, повторюсь, представления вашей индустрии о программировании меня не интересуют.

> 30+ транзакций такого типа в секунду (при такой-то загрузке).
Это и является тем самым бизнес-решением, что разделяет «задачу инвалидации кэша» от «задачи инвалидации кэша при таких-то условиях» и делает ее элементарной. А вот выбрать сколько должно быть этих самых транзакций в секунду — вот это действительно сложно, но к программированию отношения не имеет.
Это является постановкой задачи программистам на реализацию бизнес-требования. Бизнес и не должен знать не то что про инвалидацию, а даже про кеши вообще.

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

Абсолютно не важно, на каком уровне будет принято то самое ключевое решение превращающее абстрактную задачу «инвалидация кэша» в конкретную «инвалидация кэша при условии, что босс не хочет, чтобы сайт тормозил». Повторюсь, это не задача программирования. Если босс не знает, что такое миллисекунды, значит есть кто-то, кто переведет эти условия для программистов, IT-директор, например. В таком случае можно считать это задачей IT отрасли, включающей в себя, например, UX-специалистов. А вот совместными усилиями эта задача наконец дойдет до задачи программирования — описать уже готовый алгоритм, понятным компьютеру.
А то, что некоторые люди (ох, как оказалось их много), занимаясь не только программированием, решили, что теперь все есть оно, наверное, это ЧСВ, ну, или банально ошибка. Хотя, скорее, попытка выжить, ведь уже в огромном спектре задач не нужны переводчики в виде программистов, компьютер и так понимает смысл жестов, естественных языков, звуков и т.д.
Эм. То есть вы предлагаете, чтобы всякие индексы БД, алгоритмы кэширования, очереди и грин-треды изучали UX-дизайнеры и директора, а потом выдавали вам на блюдечке готовую инструкцию с описанием архитектуры программы и ссылками на описание конкретного алгоритма?
Я предлагаю называть вещи своими именами и не преувеличивать. И нет, не все задачи в IT-отрасли относятся к программированию! Но и к UX-дизайнерству или управлению тоже не относятся. Если вы не знаете как называется конкретная отрасль — не повод называть ее программированием, правда? И так странно слышать «тыжпрограммист» на ресурсе для программистов, похоже давно нужно было поднять эту тему, вон как все запустилось.
Так и как же называется человек, который формулирует когда кеш инвалидировать?
Человек может называться как угодно, главное, что к программированию это отношения не имеет.
А обычно, это человек принимающий бизнес-решения на уровне IT: менеджер проекта, IT-директор и т.д. В маленьких компаниях, очень часто, человек является и менеджером проекта и программистом, отсюда и непонимание. Но ведь если он поможет один раз собрать новые стулья, к примеру, или в компании принято каждое утро убираться на территории всем, вряд ли эти занятия станут программированием, правда?
> менеджер проекта

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

> IT-директор

А этот, на втором собеседовании, спрашивать «к кому вы пойдёте, если надо инвалидировать кэш?».

Но придумыватькак инвалидировать кэш эти двое точно не должны.
Да нет, судя по всему, это задача системных администраторов. С чего бы программисту нагружать свой мозг такой «тривиальной» задачей, как кеширование? Это наверное только админам знать и нужно. А программисту (опять же, судя по утверждениям) это абсолютно не нужно. Не нужно знать о проблеме парралельной записи и методов предотвращения испорченных данных, не нужно знать о нетсплитах — это тоже задача админа следить за сервером (хотя, пожалуй, все же программист отвечает за целосность и контроль отдаваемых данных, а не админ — но это мое субъективное мнение). И много еще «не очень интересных» для программиста проблем с кешированием и инвалидацией кеша (которая, как минимум, обеспечивает актуальность данных) связаны с кешированием.
Но тогда вопрос, если кеширование — не задача программиста, то естественно, из утверждений выходит, что ему и знать это не нужно. Так выходит что ли?
А тогда встречный вопрос, если этого не нужно знать и можно даже представления не иметь о принципах, то каким образом это будет поддерживать и кто за это будет отвечать?
И вот еще, главный вопрос, каким образом в таком формате Вы собираетесь строить кеши, которые не разезжаются с базой?
По поводу «линейности» каждой программы. Это Ваше личное мнение? Линейность предполагает выполнение одной команды за другой и этот порядок ничем и никогда не нарушается (1-2 курс университета).
А как быть с определением «нелинейное программирование»? Или, по Вашему, эти программы все равно линейны?

А то, что некоторые люди (ох, как оказалось их много), занимаясь не только программированием, решили, что теперь все есть оно, наверное, это ЧСВ, ну, или банально ошибка

Наверное за столько лет расширился спект задач, существенно прибавилось технологий, существенно выросли запросы, существенно ИТ разрослось, и не только сообщество. Любое развитие предполагает развитие и если, как программист, не понимаешь, как работает на серверах то, что использует программный продукт и с чем взаимодействует, как взаимодействует устройство с кодом, каким образом собираетесь его оптимизировать? Каким образом собираетесь укладываться в наложенные ограничения? Или Вы полагаете, что каждый заказчик желать масштабироваться вертикально? Да нет, чаще экономят.
Кэши (в ОЗУ, файлах, базах) управляются программно и реализовать управление, включая инвалидацию, задача именно программирования.
Все программы линейны.

Извините за личный вопрос, Вы случайно не закончили краткие курсы, вместо учёбы в ВУЗе?
Уж очень в Вашем сообщении сквозит невежество и отсутствие какого-либо знакомства с теорией.
Да что Вы, у человека вообще сложностей в программировании нет совершенно никаких. Вся индустрия баклуши бьет.
Это не проблема описания алгоритма, это проблема самого алгоритма. Программирование занимается переводом одних языков на компьютерные. И все. Программирование — функция, на вход которой поступает готовый алгоритм, а задача описать его компьютеру. И вся сложность заключается в том, чтобы не описывать одни и те же алгоритмы многократно. Соответственно, нас интересуют только те теории, которые позволяют сократить код (например, теория категорий). Однако, хоть эта задача и относится к единственной сложности в программировании, даже ее решают математики, а программисты лишь используют частично и подгоняют под себя.
Все по одной простой причине, мы всегда пишем программу для конкретного исполнителя (для которого уже задан набор команд), будь то процессор или виртуальная машина.
А когда пишут псевдокод, просто в голове подразумевают ряд существующих языков (написанных для конкретного процессора) и пишут нечто, для чего можно в любой момент написать компилятор.
НЛО прилетело и опубликовало эту надпись здесь
Нет, теория категорий думать и выбирать абстракции не помогает, это правда) Зато дает возможность создавать абстракции более «навсегда», чем ООП, например, что в свою очередь, для описания каждой последующей задачи, сокращает код. Т.е. нужно читать мое предложение про сокращение кода в контексте множества задач, а не вот этой конкретной.
Именно поэтому часто бывает так, что есть основная платформа, написанная на «сложных» языках, где уже определены обобщения на большинство случаев, а над ней есть какой-нибудь скриптовый язык с простейшими возможностями: так было с javascript, так есть с 1С, Siebel, так часто используется Lua и т.д. Потому что до выкристализации обобщения нужно доказать его ценность для последующих задач. В том числе и опытным путем.
И тогда выходит, что те 6 статей, что вы тут писали — пустой звук. Все и так уже поняли, что вы зеленый и собеседников слушать не хотите, в двух соседних комментариях противоречите сами себе. Так активно рассуждаете про теории, а которых, судя по набору технологий о которых вы пишите(а значит в которых разбираетесь), вы понятия не имеете.
Даже спорить не хочется с очередным JavaScript- & PHP-функциональным программистом, с одной стороны находящим проблемы даже в шаблонизации HTML и тут же говорящим об отсутсвии оных в программировании вообще.
В проектах, где количество разработчиков больше одного, задача разделения кода и отображения — одна из первоочередных


Почему нельзя написать идеальную программу

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

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

Поэтому наша программа из одной строчки гарантированно будет работать с ошибками.


По долгу службы приходится иметь очень много дел с JSON, и здесь система типов TypeScript не помогает ничем, даже мешает, ведь компилятор сообщает об отсутствии ошибок, JSON.parse возвращает тип Any. Кроме того, TypeScript не поддерживает рефлексию, ввиду специфики работы, а значит, нет возможности проверить тип, основываясь на уже существующем коде. Также, до последнего времени, средств для мета-программирования не было вовсе.

На самом деле TypeScript уже поддерживает рефлексию. Правда пока в весьма куцем виде.


А результат работы JSON.parse вы всегда можете скастовать к более конкретному типу после (или во время, благодаря type guards) валидации.

И что? Это решает все проблемы программирования и в нем их больше нет?) Цитаты из постов arvitaly которы утверждает, что программирование лишено проблем в принципе.

Просто делюсь информацией на тему. Вы чего такой агрессивный? :-)

Утро добрым не бывает :D
Эх… не занимались вы моделированием физических и химических процессов!
Сплошь линейные задачи… но какие сложные!
Спасибо. Я уже понял, что на хабре не знакомы другие понятия, кроме «программирование», которое означает все на свете, даже если вы же используете другие слова. Двоемыслие тоже давно описано, только жаль видеть его среди IT-среды.
А не надо путать программиста — человека, дающего код в ответ на требование «дай мне кнопку сделать все» и кодера — человека, реализующего написанный алгоритм :-)
Программист — это человек, который занимается программированием, все остальное — демагогия. Программирование — это процесс создания программ из формальных требований для конкретного выходного интерфейса.
Формированием формальных требований и выбором конечного устройства не занимается программирование, а следовательно и программист, хотя один человек способен заниматься несколькими профессиями (например, быть и программистом, и менеджером части бизнеса и принимать такие решения).

Кодер — жаргонное понятие, обозначающее программиста, не способного создавать собственные абстракции для повторного использования.

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

Согласен, все это путать не надо, да и в википедии (по вашей же ссылке) примерно это и описано https://ru.wikipedia.org/wiki/%D0%9F%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%81%D1%82#.D0.A1.D0.BB.D0.BE.D0.B2.D0.BE.D1.83.D0.BF.D0.BE.D1.82.D1.80.D0.B5.D0.B1.D0.BB.D0.B5.D0.BD.D0.B8.D0.B5. А на требование «дай мне кнопку сделать все» дают код в ответ не программисты, а несуществующие персонажи из сказки «Программист — бог».
либо проблем две, и это именование и инвалидация кэша.
две: именование, инвалидация кеша и ошибки на единицу.

(и я даже знаю источник)

Ошибка на единицу тоже возникает из-за желания уменьшить повторный код путем введения цикла.
Справедливости ради стоит заметить, что не в программировании, а в computer science.

Если о втором варианте, то да, формально да. Прямо скажем, в первом варианте это тоже не "сложность", это "основной императив".

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

Если изменился контракт — очевидно, что требует изменений и код, полагающийся на него. Это справедливо для любой парадигмы.

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

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

Ровно то же справедливо для любой парадигмы, опять же. Названия меняются, суть остается.
Нет никаких проблем в написание однократно используемого кода. Проблемы начинаются, когда для разных модулей/проектов/систем разные представления о SRP для данного объекта и конкретного метода.
В ООП жестко задан тип связи между методами (поведение объекта), в ФП — и эта связь является описываемой функцией. Так вот в одном модуле этот класс объекта полностью поддерживает SRP, а в другом нужна только его половина.
Мало того, каждый модуль можно рассматривать во временном промежутке. Сегодня SRP для него таков, а завтра может быть что-то изменится (тут можно спорить о качестве композиции).
И вновь напомню, что люди не одинаковые. Для одних будет SRP заключаться в этом, для других в другом.
Так вот, ФП позволяет сделать систему гибче, за счет настраиваемости связей, жестко зашитых в ООП (да, да, именно публичные, приватные, защищенные свойства, принципы наследования и т.д.). Но за эту гибкость приходится платить сложностью — мы знаем гораздо меньше заранее об одном и том же объеме кода (чем в ООП). Немногие способны компилировать в голове такой код, в отличии от, например, линейного скрипта. Поэтому детям в школе дают QBasic. Не потому, что на нем нельзя чего-то написать, а потому что на единицу объема доступный для разбора мозгом код. А кто-то способен ночью, проснувшись, прочитать чужой haskell и мгновенно понять.
Так что, выбор парадигмы напрямую зависит от ваших возможностей и потребностей, но именно в связи с совокупными возможностями ваших программистов.
Кстати, советую всем в возрасте от 35 лет (усредненно) принять свой уровень, как данность и не мучаться угрызениями совести от незнания сложных языков.
1) в ООП, как и в ФП, и даже как и в процедурке, связей ровно столько, сколько вы напрограммируете.
2) либо вы не понимаете, что такое SRP, либо я не распарсил
3) если у вас с возрастом проблемы, я сочувствую, но не стоит говорить за всех. Робу Пайку скоро 60, а Кену Томпсону вообще уже 73, однако модный у молодежи Go они как-то сделали и продолжают над ним работать.
> 1) в ООП, как и в ФП, и даже как и в процедурке, связей ровно столько, сколько вы напрограммируете.
В ООП уже зашиты некоторые из них (я описал выше какие), поэтому нет, там их меньше, в ФП приходится писать их вручную.
> 2) либо вы не понимаете, что такое SRP, либо я не распарсил
А фишка как раз в том и заключается, что нет формулы определения SRP, понятие завязано на семантике, которая не определена формально даже в естественных языках (а именно с помощью слов из естественных языков и даются названия обобщениям). Например, пресловутый стул, как оказалось может быть и сидением и предметов торговли, иметь и три ножки и вообще не иметь ножек и т.д. Создайте класс стул с его SRP и получите непонимание уже даже вашего соседа) Что уж говорить про интернациональные пакеты.
> 3) если у вас с возрастом проблемы, я сочувствую, но не стоит говорить за всех. Робу Пайку скоро 60, а Кену Томпсону вообще уже 73, однако модный у молодежи Go они как-то сделали и продолжают над ним работать.
Не понял, где тут логическая связь вообще. Я говорю всего лишь о том, что после определенного возраста обучение сложным вещам дается намного тяжелее. А то, что Пайк обладает уровнем для создания простого языка, не говорит о его возможности к созданию (или даже изучению) гораздо сложного, правда? В любом случае, ремарку про усреднение я оставил неспроста. Для кого-то это может быть и 60, но Пайк не тот пример.
семантике, которая не определена формально даже в естественных языках

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

> обладает уровнем для создания простого языка

Вообще, сделать простой язык — намного сложнее, чем сложный.

Остальное оставлю без комментариев, у вас просто каша в голове.
Кстати, советую всем в возрасте от 35 лет (усредненно) принять свой уровень, как данность и не мучаться угрызениями совести от незнания сложных языков.
Утверждение, типичное для возраста до 25 лет или ранее.
Проблема не столько в возрасте, сколько в опыте. Освоив, например, ООП и простой язык C++, для смены их на что-то другое уже нужны достаточно веские причины, поскольку новый язык придётся осваивать заново. Даже если он отличается несильно, тонкие ощущения будут другими, т.к. некоторые конструкции, которыми привык оперировать, могут отсутствовать, а для их замены есть что-то другое.
Также приходит понимание, что языков много, и изучить каждый вновь придуманный, потому что кто-то считает, что он — лучше всех, просто не хватит времени.
НЛО прилетело и опубликовало эту надпись здесь
Так не в этом ли и дело, что в более юном возрасте всякие разные новые языки учатся с большим удовольствием и большим стремлением, что ли?

Полно людей, севших 30 лет назад на C и так и не слезших с него.
В молодости интересно все новое, но потом становится достаточно эффективности.
НЛО прилетело и опубликовало эту надпись здесь
Интерес не пропадает — он меняет направление и становится более осмысленным. В юности это (было) больше похоже на метания.
Интерес не пропадает — он меняет направление и становится более осмысленным.


Он просто меняет направление. — Знаю программиста на SQL — с возрастом он стал начальником отдела и начал… писать стихи. — При встрече просил никогда не говорить с ним о базах данных и SQL.
Саморазвитие возвращает юношеские дерзания ))
А есть ещё давно севшие на COBOL, и недавно была про это статья. Если за язык хорошо платят, а работа нравится и приносит удовольствие, то нет нужды слезать с языка.
Если за язык хорошо платят, а работа нравится и приносит удовольствие, то нет нужды слезать с языка.


Тут много если. Имхо
Ну, в юном возрасте, я изучал на бумаге и CLU и Modula, и кучу других экзотических языков, и даже пытался учить PL/1, но отсутствие доступных компиляторов остановило меня.
Ну, в юном возрасте, я изучал на бумаге и CLU и Modula, и кучу других экзотических языков, и даже пытался учить PL/1, но отсутствие доступных компиляторов остановило меня.


Потом вы выучили Adabas, Foxpro, Clipper и Дельфи, завершив всё SQL и Java. Наверное.
ООП не ограничивает контракт ни сверху, ни снизу. Ограничивать могут некоторые реализации.
Господи, вы хоть поняли, что поставили выбор парадигмы программирования в зависимость от «умственных способностей программиста»? Если в ооп меняется один метод или публичное свойство, то все зависит от того, насколько меняется и как оно влияет на другие. Так же, как и в функциональном проганьи. Зачем нужно делать функцию? Чтобы её потом везде использовать. А если мы много используем функцию и вдруг её радикально изменили, обновив в том числе и параметры? Ну а чем в этом смысле отличаются методы объекта?
Касается. Так же, как и процедурного программирования. Собственно, «повторное использование» это не только про создание библиотек кода для другой программы. Это в первую очередь снижение дублирования кода внутри самой программы. Код разбивается на функции не только для улучшения читаемости, но и чтобы один и тот же код можно было повторно использовать из разных мест одной и той же программы. Это сокращает объём кода, позволяет сократить количество копапаст-ошибок и ошибок типа «там исправил, а тут забыл».

Но раз автор этого не знает, то он далёк от понимания программирования вообще, так что всерьёз воспринимать его критику…
НЛО прилетело и опубликовало эту надпись здесь
Мне обещали, что я больше никогда не стукну молотком по пальцу. Мне больше не нужны будут гвозди, есть же множество саморезов различной величины и формы! Я смогу просто взять шуруповерт и крепко прикрутить доску к другой доске!
Но когда я пытаюсь вкрутить саморез в палец, он действительно вкручивается. Когда я бью шуруповертом по гвоздю, он забивается медленнее, а шуруповерт ломается.
Прощай, шурик! Теперь я буду пользоваться молотком. (Потом камнем)
прощай Charles Scalfani, Software Engineer and Architect, Teacher, Writer, Filmmaker, Photographer, Artist… — объектно-ориентированному программированию будет тебя очень не хватать.
Тоже обратил внимание что он и на дуде игрец
Вроде прошлый таким был… Или позапрошлый…
Интересно, кто раньше умрёт ООП или я? Хоронят, хоронят его, а оно всё ни как не умирает.
И в каждой такой статье по сути одно и тоже уже. Скучно.
Да не умрёт оно никогда, в крайнем случае поменяет название. Идея хорошая и применима во многих случаях.
Каждый раз вспоминается шутка с одного it форума (ссылка на оригинал затерялась):

«Почему они выбирают между ООП и процедурами? Надо выбирать между ООП и HTTP.» © Popoff
Эта треш-статья не стоила перевода.
Особенно забавный пример с «хрупким классом». Автор добавляет в подклассе состояние (поле count), которое по его задумке должно быть консистентно с внутренним состоянием суперкласса (число элементов в нижележащем ArrayList), и при этом почему-то полагает, что ему не нужно вникать в детали устройства суперкласса.
Ну, это не проблема с состоянием, или ООП. Это проблема языка.
Если в C# ты пометил add() в базовам классе как virtual, но начинаешь его вызывать(т.е. меняется семантика) — это неявный breaking change, и свинство.
Если virtual add(int n){internalAdd(n);} — и проблемы нет.

в Java как-то не очевиднее.
НЛО прилетело и опубликовало эту надпись здесь
Как же надоели такие статьи! Человек работал 10 лет с ООП, но так и не научился его готовить? И вместо того чтобы понять когда что использовать он предлагает всё переписать на ФП. Набьёт шишек там и напишет точно такую же обличительную статью про ФП. Прям круговорот какой-то…
Вы как-то подразумеваете, что ФП менее мощная штука, которой можно обойтись. Это не так, области эффективного применения ФП и ООП — пересекающиеся множества.
«мопед не мой» (с)
Только недавно перечитывал Страуструпа и там один из вариантов решения «проблемы ромба» на С++:
class Copier: public Scanner, public Printer {
public:
using Printer::start;
}


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

То есть архитектура уже должна приводить к компоновке, а не наследованию и разведению «состояний» «в разные углы». Копир состоит из принтера и сканера, связывая их дополнительными функциями, но не наследует их свойства, как может показаться на первый взгляд.

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

Наследование — это ничто иное как агрегация, приправленная делегированием. И копир, вполне может быть наследником их обоих.


class Copier : Printer , Scanner {

    /// запускает основную функцию девайса (большая зелёная кнопка)
    start(){
        return super->Printer::start( super->Scanner::start() )
    }

    /// конкретно у копира есть дополнительная кнопка запуска отдельно принтера
    print() {
        return super->Printer::start()
    }

    /// конкретно у копира есть дополнительная кнопка запуска отдельно сканнера
    scan() {
        return super->Scanner::start()
    }

}
Для начала нужны PrinterInterface и ScannerInterface.
Тогда все становится понятно: Copier implements PrinterInterface, ScannerInterface.
Если в обоих интерфейсах методы называются start, а не scan и print, то будет плохо, но это надо было сразу думать над неймингом.
Если же методы называются print() и scan(), как у нормальных людей, то и проблем нет.

А уж наследоваться или нет — вопрос реализации. Я предпочитаю наследоваться только от абстрактных классов, а во всех остальных случаях делегировать — так явнее и безопаснее.
Для начала нужны PrinterInterface и ScannerInterface.

Не нужны. Нужна возможность наследовать интерфейсы без реализации:


class Printer { ... }
class Scanner { ... }
class Copier implements Printer, Scanner { ... }

Если же методы называются print() и scan(), как у нормальных людей, то и проблем нет.

Если знать, где упадёшь, то можно и соломки подстелить: scanWithLaser(), printToBottomTray().


во всех остальных случаях делегировать — так явнее и безопаснее.

И копипастить проксирование каждого из 100500 методов? А толку? Указывая "extends" вы фактически явно указываете проксировать всё, что не перегружено, до предка.

Интерфейсы нужны, потому что ISP.

scanWithLaser — как раз-таки нет, это уже детали реализации. Если кто-то наплодил интерфейсов с методами performAction или go, это уж ССЗБ. Имя метода должен отражать суть действия.

По поводу наследования все написано классиками в GoF — Favor 'object composition' over 'class inheritance'. Не вижу смысла пересказывать своими словами, все равно лучше не получится. Что касается «100500» — а зачем, а откуда? Всего два метода, это не больно. А если и правда 100500 — так боль уже в том, что их 100500, где-то забыли про SRP, типичный God Object. (И, кстати, копипастить надо не во всех языках).
Интерфейсы нужны, потому что ISP.

Объявление класса уже является объявлением интерфейса. В большинстве случаев нет логической необходимости его дублировать отдельно.


scanWithLaser — как раз-таки нет, это уже детали реализации.

Это конкретизация интерфейса. Такая же, как и print|scan или startPrinting|startScanning вместо start.


По поводу наследования все написано классиками в GoF — Favor 'object composition' over 'class inheritance'.

Ни чем не обоснованный догматизм.


Всего два метода, это не больно.

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


А если и правда 100500 — так боль уже в том, что их 100500, где-то забыли про SRP, типичный God Object.

Давно вы практикуете гадание по числу методов? :-)

Это не догматизм, это практика. Если мне на слово не верите, почитайте литературу. У того же Фаулера примеры из его практики.

Все, что мы знаем — что Copier должен уметь scan() и print(). Конкретные реализации Scanner и Printer могут не подходить. Может, у них у каждого независимая очередь, а нам тут нужна одна. Может, еще что. А может, сегодня подходят, а завтра нет. С делегированием такие проблемы решаются проще и безболезненнее.

По числу методов гадать нечего, это такая же объективная метрика, как и цикломатическая сложность.
Это не догматизм, это практика. У того же Фаулера примеры из его практики.

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


Конкретные реализации Scanner и Printer могут не подходить.

Это не конкретные реализации, а базовые классы, реализующие общую для всех принтеров/сканеров функциональность.


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

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


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

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

Я тоже именно предпочитаю. Но предпочитаю почти всегда, если сомневаюсь — делаю композицию, и еще никогда не пожалел. Вот о том, что сделал наследование там где не стоило бы — жалеть приходилось.

В рассматриваемом случае наследование плохо подходит, потому что Copier — это совершенно необязательно Scanner + Printer, он может быть устроен совершенно иначе. Завязаться на реализацию можно в качестве «быстрого хака». И потом огрести. :)

Разумеется, есть случаи, когда наследование абсолютно уместно. Например, что-то вроде Postgresql94QueryBuilder extends Postgresql90QueryBuilder extends PostgresqlGenericQueryBuilder extends AnsiSqlQueryBuilder.

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

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

Пусть наследуется, пусть что угодно — снаружи знать про это не надо, снаружи интерфейс.

Интерфейс МФУ = Принтер+Сканнер+Копир. Этот интерфейс предполагает, независимую реализацию принтера и сканнера. Если вам нужен только копир, а принтер и сканнер не нужен, то довольно странно наследовать его от принтера и сканнера.

> И копир, вполне может быть наследником их обоих.

«Не для науки ради, а так, чисто позырить...»

Вам тут наследование что дало?

Реализацию обоих интерфейсов в одном объекте, очевидно.

Очевидно, да. А дальше что?

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

Взяли 2 интерфейса, добавили своих методов и получили третий интерфейс (один девайс, выполняющий 3 функции).

> один девайс, выполняющий 3 функции

Т.е. профит в нарушении SRP?

Профит в появлении третьей функции, как результат объединение первых двух. Аналогичный случай:
InputRange + OutputRange = Channel.

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

> Аналогичный случай: InputRange + OutputRange = Channel.

А это с наследованием, что бы Channel к RangeBase было интереснее кастить? )

А SRP тут, кстати, не нарушается. Копир не содержит реализацию ни принтера, ни сканера — он их берёт у предка. Когда нужно будет изменить разрешение сканирования по умолчанию — будет меняться класс Scanner, а не Copier.


А зачем вам кастить Channel к BaseRange?

То есть Copier можно отнаследовать ещё и от листа бумаги(холодильника и Array), и пусть печатает сам себя — это не нарушение SRP?

То есть профит от наследования — в возможности создать god object «не нарушая SRP»?

Можно, если вам это почему-то нужно. Хотя, я затрудняюсь сказать зачем такое может понадобиться. Но нарушением SRP это опять же не будет.


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

> Можно, если вам это почему-то нужно.

Наследовать не «нужно». Никогда.
Я вот от вас не могу добиться зачем вы отнаследовали 2 несвязанных класса.

> Божественный объект — это объект, который содержит не связанную между собой реализацию

И это именно то, что вы делаете.

Постановка задачи такая — нужен МФУ.


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

> Постановка задачи такая — нужен МФУ.

Мы ещё ваш код копира обсуждаем? Это не МФУ. На копире «scan» мне не нужен.
Если нам нужен МФУ — у вас будет, например, 3 свойства *Settings: от принтера, сканера и МФУ. Это монстр Франкенштейна, а не МФУ.

> В случае наследования, реализация находится в родителе.

Если вы про god object — не имеет значения. God object — это проблема не реализации, а использования. Для внешнего кода не важно как именно класс агрегирует функционал всего.

> В этом, собственно, и суть наследования — хранение общего поведения в отдельном классе (классах).

Да ну? Для «хранения общего поведения в отдельном классе» наследование не нужно от слова «совсем»

Нет смысла делать чисто копир, если можно сделать мфу. Но если нужен только копир, то наследование тут не нужно. Как, впрочем, и композиция сканера и принтера в общем случае.


Корневой неймспейс — вполне себе божественный объект в вашей интерпретации.


А никто не говорит, что оно нужно. Оно удобно.

> Нет смысла делать чисто копир, если можно сделать мфу.

Нет смысла не сделать god object, понимаю.

> Корневой неймспейс — вполне себе божественный объект в вашей интерпретации.

В моей интерпретации неймспейс — не объект. Вы ещё и объект от неймспейса не отделяете?

> А никто не говорит, что оно нужно. Оно удобно.
> Можно, если вам это почему-то нужно.

Ох…

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

У вас богообъектофобия? Вы видите их там, где их нет.


Не отделяю. Я ещё и классы от объектов не отделяю. Вообще пропащий человек.


А в чём удобство от повторения делегирования каждого метода?

> А в чём удобство от повторения делегирования каждого метода?

— Доктор, когда я вот вот так вот делаю у меня болит..((
— А вы вот вот так вот не делайте
НЛО прилетело и опубликовало эту надпись здесь
babylon может статью с подробными пояснениями про нода-контейнеры напишете? *стало любопытно*
НЛО прилетело и опубликовало эту надпись здесь
Если начать с введения, что такое Нода итп, то на статью вполне потянет.
НЛО прилетело и опубликовало эту надпись здесь
Вам тут наследование что дало?

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

… а для этого не надо наследования. Для этого надо выставить публичный интерфейс "принтер".

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

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

То, что я не написал о реализации, не значит, что я о ней не думал. Слово «просто» из предыдуцего комментария относилось и к ней тоже.

Я понял интерфейс, как в Java. Там он не имеет никакой реализации по определению. Конечно, в других языках может быть возможность определить для объекта интерфейс с реализацией, способом отличным от наследования, но зачем?
Конечно, в других языках может быть возможность определить для объекта интерфейс с реализацией, способом отличным от наследования, но зачем?

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

Для меня основная проблема множественного наследования — его отсутствие в большинстве языков.)

Это пока вы не начали получать проблемы с неопределенным поведением.

Ну, раз 20 лет не получал, может и ещё 20 не получу.)
О, а раскройте схему наследования факса. Ну, с учётом того, что там сканер протяжной, а принтер печатает на отрезных кусках рулона (т.е. реализация у них несколько иная, чем у МФУ).
Факс в составе МФУ может сводиться к розетке RJ11 с интерфейсной платой. В рамках данной дискуссии это несущественно.
Ну, наверное, может. Хотя, я всегда факс видел на столе, а МФУ — отдельная тумбочка. Вот мне и было интересно каким наследованием это должно делаться.

Хотя, без наследования — тоже вариант.
Т.е. вместо
Add(Copier.GetPrinter())
можно написать
Add(Copier)

Ну, принимается. Хотя ценность так себе.

… для этого, впрочем, наследование не нужно, нужно Add(IPrinter) и Copier: IPrinter (или implicit operator IPrinter(Copier copier))

Это где? в C# implicit operator к интерфейсу, вроде, нельзя.

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

Add(IPrinter) может от меня не зависеть, а implicit operator — это всё же костыль, которым лучше не злоупотреблять.

Я вообще не уверен, что хочу иметь на копире интерфейс принтера. I feel disturbance in Force.

Скорее MyUberDevice: IPrinter, ICopier

Ну так если Add не IPrinter, то мы и так в печали уже. А если оно таки IPrinter, то откуда мы это выставили — уже не важно, важно, что нас дальше не волнует, наследовались мы от какого-то принтера, или нет.

Хотя ценность так себе.

Для меня важно, что в случае наследования в список добавляется весь объект, а не его составная часть.
почему?
Эстетичнее.
Вопрос был в общем про ромб (diamond problem).
Компоновку — хорошо — можно предоставить и как наследование при соответствующих возможностях языка, однако как бы мы компоненты ни заворачивали — они должны быть изолированы друг от друга, иначе они друг друга «покрошат», поскольку у них есть похожие части, которые выглядят одинаково, но отвечают за разные состояния и переопределённые в наследнике эти части могут удивить родителя. В примере выше — кроме отдельной кнопки — есть ещё отдельные внутренности, например, сервопривод. Если же всё это изолировано и работает независимо — смысл склеивать всё в один класс, чтобы потом всё время писать «такая-то часть класса» — уже дискуссионен. Но «осуждать» не зная особенностей конкретного языка — воздержусь.
НЛО прилетело и опубликовало эту надпись здесь
В примере из статьи, скорее, просто проблема именования: название метода должно отражать процесс, то есть start — это подготовка устройства, которая вполне может происходить и параллельно для составных частей копира, а для печати/сканирования/копирования метод должен называться соответственно print/scan/copy, возможно с префиксом start_, если мы хотим не ждать окончания процесса, а получить отдельное сообщение о результате.
Копир должен именно наследовать принтер и сканер, поскольку может использоваться как эти отдельные устройства, а не только их комбинация.
Сам по себе разумный подход к ООП не опасен. Опасность в инструментах, которые слишком многое скрывают от программиста, а их использование обычно означает религию определенного способа мышления и подхода к решению задач.
Лично у меня большие проекты, где основными элементами дизайна (архитектуры) являются сомны сложных объектов вызывающие методы друг друга, а не подсистемы с ограниченным функционалом и конкретным явным уровнем абстракции данных, связанные протоколами различных уровней вызывают головную боль и рвотные позывы.
Особенно когда видишь a++ на верхнем уровне и понимаешь, что сейчас придется пройти через сотню унаследованных классов и операторов, чтобы понять, что здесь происходит :(.
Проблема не в ООП как в таковом, а в том, что в ОО-ЯП внедрены все возможные инструменты, которые позволяют стрелять себе в ноги, только ради «полноты возможностей ООП». Не всегда всё, что красиво в теории, надо воплощать на практике. Нужен обрезанный ЯП (или его стиль), который не позволяет неокрепшим девелоперам лажу гнать, а не переход к другой парадигме, где будут присутствовать все те-же проблемы.
Что-то вроде Паскаля (или даже Дельфи), но для ООП?
Да, ведь Java и возникла, чтобы сделать сложное (С++) простым. Но не было опыта, поэтому сделали по максимуму. Теперь надо ее минимизировать, чтобы «хоп-хоп и в продакшен» еще легче делать было. И это не только Java касается…

Конечно, сам подход, что программисту надо о чем-то думать заранее, уже несовершенен. Но даже в ФП он присутствует…
Проблема ромба не в том, что бы выбрать какую функцию start дергать. Проблема в том, что копир по кнопке «старт» не копирует (Карл!).

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


Class Copier {
    Scanner scanner
    Printer printer
    function start() {
        page = scanner.start();
        printer.start(page);
    }
}

Да и остальные примеры высосаны из пальца. Как уже неоднократно говорилось в коментариях к подобным статьям: плохая архитектура не проблема парадигмы.

Ну, типа того. Осталось во всех 3-х классах переименовать start.
А зачем дополнительное инстанциирование?

function start()
{
    Scanner::start();
    Printer::start();
}

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

Конкретно в тексте у автора проблема именно в том, чтобы выбрать какую из функций start дёрнуть.
Обратите внимание, что классы Scanner и Printer оба реализуют функцию start. Тогда какую функцию start унаследует класс Copier? Та, что реализована классом Scanner? Или классом Printer? Не могут же они обе унаследоваться.


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

Я бы поставленную задачу решал как-то так:
class Copier: public Scanner, public Printer {
public:
void start() { Printer::start(); Scanner::start(); }
using Printer::print;
using Scanner::scan;
void copy() { Page *page = this->scan(); this->print(page); }
}
> Software Engineer and Architect, Teacher, Writer, Filmmaker, Photographer, Artist

Какой разносторонний товарищ. Мне кажется, ФП ему нужно чисто в коллекцию титулов…
Статья для *banoe.it, а не хабра.
Такое там еще сильнее обольют.
А вообще интересно такие статьи читать.
Во-первых, хоть что-то про программирование
Во-вторых, когда показываются недостатки, или проблемы, возникающие при попытке использования тех или иных средств — сразу задумываешься, а почему так и можно ли улучшить что-то в самом ООП чтобы эти особенности обойти?
Больше радует осознание факта, что твой уровень знаний достаточен, чтобы сразу понимать, где автор написал фигню, и почему это фигня.
НЛО прилетело и опубликовало эту надпись здесь
Господи, какое убожество. Он изобрёл мультинаследование и говорит при том, что наследование — это плохо. Проблема ромба решается тривиально во многих языках. Где не решается — это вопрос реализации (языка), а не концепции ООП. Вообще концепция ООП не тождественна C++, есть например ещё smalltalk, где всё совсем по-другому.

Полиморфизм — не есть уникальное свойство ООП, каждая парадигма предлагает полиморфизм, просто реализации разнятся. ООП имеет естественный полимформизм, но может пользоваться и функциональным полиморфизмом, просто не бесплатно.
Читал в оригинале, увидел на хабре, думаю, кто ж решился этот трешак переводить… Смотрю: Mail.ru и даже не удивился — как всегда уверенно пробиваем очередное дно.
Думаю много проблем в области ООП растут из того, что «умные делают а глупые учат». 99% всех объяснений ооп содержат в себе те самые 3 столпа, когда на самом деле их не три. Это лишь попытка систематизировать свойства ООП. И эта попытка стала очень популярной и отсюда куча однотипных и неверных примеров наследования. Да, вначале это кажется чем-то божественным, но потом, оказывается что надо отринуть привычное и делать композицию. Это кажется противоестественным потому что везде учат наследовать.

Буллшит-бинго!

все описанные проблемы решаются интерфейсами. Бтв, множественное наследование практически везде лучше/проще заменять на композицию
Документы и компании для обрушения «столпа наследования» — классический приём демагогии: привёл два понятия, не связанных отношением «общее-частное» и на основе этого объявляем само отношение ненужным. Есть же случаи, когда это отношение работает, например, документы и отчёты, письма, заявления и т.д.

Я открыл статью, задавшись вопросом: «А что взамен?», но так и не понял, чем так замечательно ФП и почему автор выбрал именно его.
Тем, что там автор еще не «все понял».
Понятно. Не осилил ООП — пошёл неосиливать ФП.
Goto живо, а они ООП хоронят :)
Человек решает проблему иерархии переводя её в плоскость группировки и классификации… ЧуднО однако
в Ruby все объект
Нужно сочетать OOP и COP, не хочешь вникать в устройство класса и наследоваться, считай его компонентом.
Иногда лень писать шаблонные методы, пользуюсь копипастом, но я с этим борюсь, в случае с ФП копипаста было бы намного больше.
Божечки-матрешечки! Сколько уже можно? Предположу, что автор статьи через N-лет напишет «Почему ФП мертво», так и поняв, что мертво не ФП или ООП, а он, как инженер. Проблемы начинаются с самого начала, потому что автор подумал, что ООП — это способ переноса объектов реального мира в код, что само по себе, на мой взгляд, не очень правильно. Начало понимания ООП, на мой взгляд, как раз начинается с понимания того, что этого делать чаще всего нельзя. Попытка оправдать свою кошмарную архитектуру недостатками парадигмы(которы, конечно, безусловно есть) обречена на провал. Хороший инженер отличается от плохого тем, что может используя доступные ему инструменты делать качественные продукты. Автор же похож на типичную ТП, купившую зеркалку и возмонившую себя крутым фотографом, которой не вдомек, что качество твоей работы от фотоаппарата зависит далеко не в первую очередь. Что бы было понятней — вот фото конца 19 века, когда фотографам современные технологии даже в самых смелых мечтах и приснится не могли.
image
Это не фото 19 века, а картина Arch of Constantine (Rome), нарисованная Бернардо Беллотто в 18 веке
Какой век, такие и фото.
Ахах! Глупо вышло, не знаток искусства и фотографии(я же все-таки программист, а не фотограф), но я думаю, вы и сами без труда сможете найти старые фотографии, или очень красивые фотки снятые на обычные мыльницы или телефоны. Спасибо, что сказали, теперь запомню надолго, что это именно Бернардо Беллотто.
Потерянное поколение XD
Можно было выкрутиться, сказав, что это фотография картины. Прокси-объект… если говорить в терминах ООП.
«Демократия имеет много недостатков, но лучшего человечество не придумало» (с) У.Черчилль

А если по существу — всё уже сказано выше. Автор статьи строит кривую архитектуру, после чего начинает ругает ООП за то, что архитектура получилась кривой. Если б я со своим почти нулевым опытом ФП бросился писать статью о ФП — от ФП вообще камня на камне не осталось бы. Кстати, о самом ФП автор пишет только, что ФП рулит… И всё (впрочем, это тоже уже заметили комментирующие выше).

П.С.: Печально что такая серьёзная компания, как mail.ru допускает в своём блоге публикацию столь маргинальных статей (ну, или переводов статей — без разницы).
Цитата Черчилля звучит немного по-другому:
«Демократия — наихудшая форма правления, если не считать всех остальных.»
Основная проблема ООП — что оно кажется простым, а на самом деле очень сложное. (Функциональщина наоборот, кажется сложной, но на деле проста.)
Если бы программисты понимали принцип подстановки Лискофф и знали, что такое коммутативные диаграммы в теории категорий, то проблем бы не было.
И, кстати (в поддержку ФП), с иммутабельными объектами наследование работает лучше, с мудательными принцип подстановки соблюдать сложнее.
Я конечно могу ошибаться, но разве инкапсуляция, полиморфизм и наследование имеют отношение к ООП? Да, это хорошие приемы, но это не обязательная часть ООП. Все эти принципы можно реализовать на не ОО языке. И ОО язык не обязательно будет обладать встроенными механизмами для их реализации.
ООП — это это не классы, это когда объекты обмениваются сообщениями.
Я бы только добавил, что обмениваются сообщениями в соответствии с контрактами (это важно). А так целиком согласен.
О каких контрактах речь? Почему это важно? Вы же не хотите сказать, что ООП не может быть без контрактов?
Потому что обмениваться произвольными сообщениями с кем попало довольно бессмысленно.
Ну вот erlang OTP так делает и не особо страдает. Да и вообще больше половины языков не имеют статической типизации, и ничего — живы.
…И Smalltalk так же делает. И даже имеет DoesNotUnderstand по этому поводу — мощнейшая вещь, показывающая что очень даже не бессмысленно.
А я не про типизацию. И erlang/smalltalk/ruby/etc — это не важно.

Бессмысленно просить кофеварку помыть посуду, а пылесос сварить кофе.

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

А по поводу ООП на C можно ответить так: прежде, чем вы сможете на C писать объектные программы, вам придется сначала создать соответствующую «инфраструктуру» (те же объекты + механизм посылки сообщений и связывания их с кодом) — другими словами, написать объектный «DSL». Так что без языка — увы — все равно ничего не выйдет.
Кажется, вы слишком буквально воспринимаете «отправку сообщений». Это не о реализации, а о принципе. О способе думать.

Вот в Objective C технически отправка сообщений, а в Swift-е — технически вызов метода. И ничего, ООП-программы писать можно и на том, и на другом.

На С можно обойтись структурой с указателями на функции и договориться первым аргументом передавать this (привет, python).
Кажется, вы слишком буквально воспринимаете «отправку сообщений».

Вы хотите рассказать мне о том, как я это понимаю? :) Ну, ок, давайте…

Вот в Objective C технически отправка сообщений, а в Swift-е — технически вызов метода.

Что такое «технически отправка сообщений»? И что такое «технически вызов метода»? Я-то по простоте душевной думал, что независимо ни от чего, получивший сообщение объект должен как-то связать этот факт с кодом, который это сообщение должен обработать. Оказывается — нет. Очень-очень интересно, продолжайте.

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

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

Принципиальная разница, в том, в какой момент связываем сообщение с кодом-обработчиком — compile time или run time.

Хотя на самом деле все сложнее. Например, компиляторы Objective C могут оптимизировать «сообщение» в «вызов метода», если во время компиляции обрабатывающий код очевиден. Когда (и если) в PHP сделают JIT, там наверняка будет то же самое.

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

> И что же это будет, если не язык?

На уровне языка — структура и указатели. А объект и сообщение — у нас в голове.
Принципиальная разница, в том, в какой момент связываем сообщение с кодом-обработчиком — compile time или run time.

Ох, я начинаю жалеть, что вступил в беседу. Давайте вы сначала разберетесь хотя с тем, что передача сообщения не может быть реализована в «compile time», хорошо?

…А потом, надеюсь, и с другими понятиями. Удачи!
Да, я тоже зря вступил с вами беседу. Давайте вы сначала будете читать комментарии целиком и разберетесь, чем парадигма отличается от технической реализации, а потом, надеюсь, и с другими понятиями. Удачи!
НЛО прилетело и опубликовало эту надпись здесь
«Вы не любите кошек? Да вы просто не умеете их готовить!»

Заметил такую особенность в своём коде — почти все поля классов readonly, за исключением классов для данных.
Как-то само собой стало так получатся, может поэтому нет проблем про которые пишет автор?
Если нужно организовать in-memory DB использую разделяемые списки, как я понимаю чистое ФП этого не может, а вот мне надо.
Так что ООП никуда не денется.
Прям ФП каминг-аут какой-то :)

Процитирую статью с Хабра:
Алан Кэй, создатель ООП, про разработку, Лисп и ООП


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

Может кто-нибудь объяснить мне подробнее «Проблему ссылки (The Reference Problem)», а то автор статьи как-то не очень понятно ее расписал? а в поисковиках что-то нашел, но не то
Может кто-нибудь объяснить мне подробнее «Проблему ссылки (The Reference Problem)»


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

Вам надо создать копию объекта B (B') и передать ссылку при создании объекта A на эту копию (на B'), а самим спокойно использовать данные объекта B.

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

Например, на С++:
Ничего не мешает передавать в А константную ссылку на В, если нужны 100% гарантии*, что данные в В не изменятся. В данном случае, если конструктор А требует неконстантную ссылку, то проект не скомпиляется — будет возможность продумать дальнейшие шаги по устранению до запуска программы
Если объект А все-таки принимает неконстаную ссылку, значит он меняет что-то в В, значит создание А должно сопровождаться ожиданием, что в В что-то может измениться
Объект А для своих внутренних нужд может изменять только свои приватные данные, а не внешние, т.е. В в данном случае
То есть, при самом обычном (даже не самом хорошем) проектировании указанной проблемы с ссылкой не должно возникнуть, в принципе

*100% гарантии все равно не будет, т.к. существует const_cast, но это крайний случай, который опять-таки вытекает из проектирования. Константные ссылки, константные методы и т.д. придумали не просто так
наследование является ключом к повторному использованию

Нет
Но переданный объект небезопасен!

Нет
Так что без лишних разговоров прощаемся с ОО-полиморфизмом, и приветствуем полиморфизм на основе интерфейсов.

Простите, а ОО-пролиморфизм он не на основе интерфейсов?
Простите, а ОО-пролиморфизм он не на основе интерфейсов?
Возможно ключ в том, что автор писал только на C++, где ключевых слов для интерфейсов нет.

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

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

… и где вторая половина статьи?

Где вторая половина статьи, где объясняется, почему именно ФП, а не скажем ProLog или ещё что-то иное?
Как-то все слишком косно.

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

Все это в сумме очень ускоряет разработку. По отдельности — ломает мозг.
Небольшой обзор статьи в новый тэг #кунсткамера

> Эта проблема — ещё одна трещина в Столпе Наследования.

Это трещина не в наследовании, а в башке автора

1) ООП не предназначено для прямого уменьшения повторного использования, оно предназначено для корректного моделирования системы. Повторное использование если вообще и бывает, является побочным эффектом.

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

Соответственно, сама система должна быть спроектирована для увеличения повторного использования. А не говнокод которым ты это всё замоделил. Чтобы система была правильно спроектирована нужен хороший разработчик систем, например выделенный системный аналитик или архитектор.

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

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

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


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

А потом прошло 30 лет.

3)
> Я в течение десятилетий программировал на объектно-ориентированных языках.
> private ArrayList[Object] a = new ArrayList[Object]();


Вместо private List используется private ArrayList. Абстрагирование, полиморфизм подтипов — не, не слышали. Загримированный под матерого ООП кодера, Штирлиц никогда не был так близок к провалу.

4)
> Возьмём следующий базовый класс
> public class Array { private ArrayList[Object] a…

Знаете, чем хороша Java? Тем что в ней, к счастью, нет такого базового класса.

Автор написал поверх класса List прокси с названием Array (sic!), и запроксировал в нем метод add(element). Потом запроксировал addAll, в котором полностью проигнорировал тот факт, что addAll и add это не какие-то произвольные операции, а жестко связанные. В addAll уже нельзя использовать оригинальный add, нужно использовать свой прокси. После столь свинского с собой обращения, всё похерилось. Конец был немного предсказуем, верно?

5)
> Мы должны работать только с интерфейсом. Это раздражающая тенденция...

… раздражающе мешающая писать говнокод, ясно.

6)
> Идея иерархии “общее/частное”…
> Каждый раз, создавая новую компанию, мне приходится решать проблему, связанную с выбором места для документации компании. Нужно ли мне сделать папку Документы, а внутри неё создать папку Компания? Или мне нужно сделать папку Компания, а внутри неё — Документы? Оба варианта рабочие, но какой из них правильный? Какой лучше?


Вы действительно хотите сделать лучше? Тогда вот мой совет как вашего адвоката:

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

7)
> иерархия “общее/частное” не работает. Так для чего тогда хороши иерархии?
> Для включения (Containment).


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

С 1 сентября 1960 года (или когда там в продакшене появится ООП), писать на нем разрешается только отряду Мстители под руководством отца Тони Старка. (Marvel не похоже на людей, свято блюдущих хронологию)

8)
> Инкапсуляция, второй обрушившийся столп

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

9)
> The Reference Problem
Автор сам придумал этот термин?
Я вижу только https://en.wikipedia.org/wiki/Reference_class_problem
и это вообще из статистики, а не программирования

10)
> Если ссылка на объект передаётся конструктору объектов, то он кладёт её в приватную переменную, защищённую инкапсулированием. Но переданный объект небезопасен! Почему? Потому в какой-то части нашего кода содержится указатель на объект, то есть код, вызывающий конструктор. Но он ДОЛЖЕН иметь ссылку на объект, в противном случае он не сможет передать её конструктору.

Автор скорей всего как бы хотел сказать о паттернах fluent interface + builder, примененных в concurrent случае? Что кроме кроме религии запрещает ему сделать все доступы безопасными?

11)
> Конструктору придётся клонировать переданные объекты.

А, автор решил нам продать defensive copying! Об этом рассказывают на первом курсе универа на предмете «программирование на языке высокого уровня» (ПЯВУ). Так что, кто не пробовал поступить даже на первый курс, или никогда не читал классические тексты по Java, или википедию, или вообще ничего, эта информация безусловно будет вам очень полезной.

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

Ну а невозможно это потому что? Да черт его знает. Я уже задолбался писать этот комментарий, если честно :-)

12)
> Полиморфизм, третий обрушившийся Столп
> Куда бы они не отправились, он был с ними, но всегда на вспомогательных ролях.


Барбара Лисков и Джанет Винг, Андрей Александреску и Герб Саттер, Роберт «Uncle Bob» Мартин, Девид Хейден и Бертранд Мейер, и другие, менее популярные товарищи, плачут кровавыми слезами и крутятся в постелях как вентиляторы.

13)
> Блог компании Mail.Ru Group

Мэйлру капец. Ясно.

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

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

Опять! Недели не проходит, чтобы кто-нибудь не стал наезжать на ООП.


Появился новый проект, я не забывал о своей идее с классом и испытывал большой энтузиазм. Без проблем. Повторное использование спешит на помощь. Нужно просто взять класс из другого проекта и применить его. Ну… вообще-то… не просто класс. Понадобится родительский класс. Но… Но это всё. Гхм… погодите… кажется, нужен будет ещё родитель родителя… А потом… в общем, нужны ВСЕ родители. Хорошо… хорошо… Я с этим разберусь. Без проблем.

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

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

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

В общем, ждем статью «Прощай, функциональное программирование».
НЛО прилетело и опубликовало эту надпись здесь
Сотрудники мейл.ру изложили свои мысли здесь: http://mailru.group/ :)
Бытует мнение, что если бы автор придерживался солид принципов, то вероятно смог бы избежать доброй части крашей
Вывод статьи меня несколько обескураживает. Ведь ФП не исключает ООП.
Идеала не существует по определению. Можно только идеально применять отдельные инструменты для конкретных задач.
Вообще-то статья о том, что ООП тоже должно развиваться с учетом практического применения (например в сторону уменьшения стрельбы в ногу). Вот вывод непонятен — при чем здесь ФП, если ООП еще не исчерпало себя? Может просто надо выдвинуть определенные требования к ОО-ЯП быстрее меняться?
Ну а ФП — это вовсе не следующий, последовательный шаг в развитии, это параллельная ветка. Которая возможно когда нибудь и объединится с ООП,
Эта статья — детский сад, парадигма не может быть виновата в своих недостатках, она может быть выбрана на ту или иную задачу, или совокупность задач. Надо заранее предполагать, какая более удобная в каждом случае должна быть выбрана парадигма. К примеру, Web же не кодят на bash. Думаю автор непрофессионален.
Основная ошибка автора — ни один язык сам по себе ничего не гарантирует. Парадигме надо следовать.

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

Проблема хрупкого класса не являтся проблемой ООП. Это просто — антипаттерн. Такой же, как, например, GOD-object. Не надо так писать. А если почему-то надо, то стоит запретить наследование таких классов.

Вот тут , например, автор хорошо показывает, что стрелять себе в ногу можно и на функциональном языке. Было бы желание.
У меня плохие новости. Дело в том, что выявление общих свойств у нескольких объектов требует IQ. Тесты на IQ составлены таким образом, что проверяют именно умение выявлять общие закономерности в приведённых частных проявлениях. Чем выше IQ, тем более сложные и запутанные кейсы человек может распутать. Так что если кому-то кажется, что иерархия «общее/частное» не работает, то скорее всего проблема не в иерархии, а в чём-то другом.

Т. е. статья о том, что если вы пытаетесь с помощью ООП сделать какую-то херню, получается херня? Ок.

Градус неадеквата статьи и отдельных веток комментариев просто зашкаливает.

https://www.youtube.com/watch?v=HTLu2DFOdTg — я просто оставлю это здесь.
Я все комменты не осилил, возможно пропустил… Но таки никого не коробит от этих «трех столпов ООП»? Абсолютно все на самом деле полагают, что ООП — это наследование, инкапсуляция и полиморфизм?!
Пикантность ситуации в том, что каноническое определение ООП на классах звучит так: ООП — это парадигма программирования с использованием объектов, которые являются экземплярами классов, которые в свою очередь образуют иерархию (т.е. наследуются друг от друга).

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

Ну и касательно повторного использования — если вспомнить времена Turbo Pascal 5.5 и становления Java — основная киллер-фича ООП, которая хорошо покупалась бизнесом, звучала именно как «возможность повторного использования кода». Это исторический факт.

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

Такие дела.
Наследование != повторное использование.

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


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

Я не знаю кто и в каком языке впервые ввел ООП, но одним из самых известных пионеров, кто начал его популяризаю, думаю, был Алан Кэй. Он утверждал, что основной задачей данной парадигмы, является уход от статической модульности, в виде поключаемых файлов, к динамическим модулям в виде объектов, реализующих как обобщенную, так и конкретную функциональность. Он никогда не говорил, что основной сутью ООП являются объекты как таковые, но именно инкапсуляция в объектах независимых частей общей системы (откуда и растут ноги многократно используемого кода) и взаимодействие данных частей. Главной задачей была именно динамичность системы: горячие обновления путем замены объектов в runtime, как, например, обновление в erlang, взаимодействие модулей, основанное прежде всего на интерфейсах, обеспечивающих гибкость и легкость изменения объектов без нарушения работоспособности системы в целом, общение объектов между собой посредством сообщений, что давало возможность общим объектам делегировать выполнение задачи более специализированным объектам и многое другое.

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


Вот это новое слово в истории IT! Оказывается, нам всем врали, что этот термин придумал какой-то там Alan Kay. Слова, оказывается, народные :)
Kay is one of the fathers of the idea of object-oriented programming, which he named, along with some colleagues at PARC and predecessors at the Norwegian Computing Center.


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

Вот именно: «which he named». Да и другими источниками иногда пользоваться не мешает. Он сам неоднократно называл себя изобретателем термина, описывал при каких обстоятельствах это произошло. А также объяснял, что именно под этим термином понимается. И, например, такие слова как «инкапсуляция», «полиморфизм» я в его высказываниях не припомню.
Другие люди тоже с удовольствием много раз говорили о нем как об авторе термина. И я не видел никого, кто бы пытался уличить его во вранье по этому поводу. Если вам что-то известно об этом — будет интересно узнать, что именно.

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

А вот с этим я абсолютно согласен.
Открываем любую книжку для посвященную ООП для начинающих в Java или C++, например Эккеля или Лафоре и в первых же главах, посвященных наследованию и полиморфизму мы видим указание на проблемы множественного наследования, и на проблемы последовательной инициализации классов при создании конструкторов потомков. И самое главное, о чудо! Мы видим решение этих проблем…
Но зачем нам читать такие книжки, они же для новичков, а мы же «в течении десятилетий программировали на ООП». Лучше я напишу свою статью, где выведу на чистую воду языки, ставшие промышленным стандартом программирования…
Маразм крепчал…
А если по статье: слабая аргументация, практически все можно опровергнуть. Про полиморфизм дак вообще ничего внятного я не увидел.
Статьи подобного направления выходят с завидной регулярностью, и народ накидывается яростно спорить в комментариях. Как в анекдоте про негров и баскетбольный мяч. Люди, не ведитесь…
Пример с Array и ArrayCount возможно может встретиться, но в реальном проекте так писать не надо. Во всех технологиях есть подводные камни о которых стоит подумать прежде чем что то делать.
Как уже писали
Либо несколько изменить ArrayCount, либо оба Array и ArrayCount(если есть возможность).
Что то мне подсказывает, что в текущей реализации будут и другие проблемы с использованием и ожидаемым результатом.

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

Надеюсь только, что когда автор разочаруется и в функциональном программировании (а это с его ожиданиями от жизни — вопрос времени), он не поленится на такой же расширенный вброс, с примерами, блок-схемами и картинками. И желательно в какую-нибудь пятницу. Ну и с вектором своих дальнейших действий. Чтобы, так сказать, сохранить интригу объективности.
Так и представил: сидят менеджеры-маркетологи-директора-соучредители, думают, как бы им ещё попиариться, и тут уборщица заходит мыть полы и говорит: «А давайте напишем на хабре, что плюсы и ява — х-ня». Они ей: «Напишешь?». Она: «Фигня-вопрос» (а она пока полы мыла 20 лет в офисе, наслушалась жалоб программистов, типа «Как достал этот ооооп, завтра — точно уволюсь, и на фп перейду… только сначала кредит отдам и ипотеку»). Извините, если не смешно :)
Если так много комментов тема собрала, значит пусть пишет ))
Мне очень нравится функциональный подход, кайфую когда пишу код. Но с автором не согласен. Это же банальные вещи, у каждой парадигмы свои плюсы, свои минусы, сотню раз обсуждалось. Для каждой задачи свой инструмент. А в «чистом» виде, зачастую ни одна парадигма не жизнеспособна, за исключением редких «экстремальных» случаев.

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

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