Прощай, объектно-ориентированное программирование

Original author: Charles Scalfani
  • Translation


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

Я не мог ошибаться сильнее.

Наследование — первый павший Столп




На первый взгляд, наследование — главное преимущество парадигмы ООП. Все приводимые нам упрощённые примеры иерархий, казалось, имели смысл.



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

Проблема бананов, обезьян и джунглей


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

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

Ну замечательно. Теперь не компилируется. Почему??? А, понятно… Этот объект содержит этот другой объект. Так что он мне тоже нужен. Без проблем. Погодите… Мне не нужен всего лишь тот объект. Мне нужен его родитель, и родитель родителя, и т.д. Для каждого вложенного объекта мне нужны родители, а также их родители, и их родители, родители… Мда. Джо Армстронг, создатель Erlang, когда-то сказал прекрасные слова:

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

Решение проблемы банана, обезьян и джунглей


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

Проблема ромба


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



Большинство ОО-языков это не поддерживают, хотя оно и выглядит вполне здраво. Так в чём дело? Посмотрим на этот псевдокод:

Class PoweredDevice {
}

Class Scanner inherits from PoweredDevice {
    function start() {
    }
}

Class Printer inherits from PoweredDevice {
    function start() {
    }
}

Class Copier inherits from Scanner, Printer {
}

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

Решение проблемы ромба


Решение очень простое: не делайте так. Вот именно. Большинство ОО-языков не позволят так сделать. Но… что если мне нужно смоделировать такую ситуацию? Мне нужно моё повторное использование! Тогда вы должны агрегировать и делегировать.

Class PoweredDevice {
}

Class Scanner inherits from PoweredDevice {
    function start() {
    }
}

Class Printer inherits from PoweredDevice {
    function start() {
    }
}

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

Класс Copier теперь содержит экземпляры Printer и Scanner. Он делегирует реализацию функции start классу Printer. А может так же легко делегировать Scanner’у. Эта проблема — ещё одна трещина в Столпе Наследования.

Проблема хрупкого базового класса


Итак, я уменьшаю свою иерархию и не позволяю ей становиться цикличной. Проблема ромба решена. И всё снова стало хорошо. Пока вдруг… Однажды мой код перестал работать. При этом я его не менял. Ну, наверное, это баг… Хотя, погоди… Что-то изменилось… Но этого не было в моём коде. Похоже, изменение произошло в классе, от которого я наследовал. Как изменение в базовом классе могло сломать мой код??? А вот как… Возьмём следующий базовый класс (он написан на Java, но вам будет легко разобраться с кодом, даже если вы не знаете этого языка):

import java.util.ArrayList;

public class Array
{
    private ArrayList<Object> a = new ArrayList<Object>();

    public void add(Object element)
    {
        a.add(element);
    }
 
    public void addAll(Object elements[])
    {
        for (int i = 0; i < elements.length; ++i)
            a.add(elements[i]); // this line is going to be changed
    }
}

ВАЖНО: Обратите внимание на выделенную строку. Позднее она изменится и всё сломает. У этого класса две функции интерфейса: add() и addAll(). Функция add() добавляет одиночный элемент, а addAll()несколько элементов посредством вызова функции add(). А вот производный класс:

public class ArrayCount extends Array
{
    private int count = 0;
 
    @Override
    public void add(Object element)
    {
        super.add(element);
        ++count;
    }
 
    @Override
    public void addAll(Object elements[])
    {
        super.addAll(elements);
        count += elements.length;
    }
}

Класс ArrayCount — специализация общего класса Array. Единственная разница в их поведении заключается в том, что ArrayCount содержит счётчик количества элементов. Давайте подробнее разберём оба класса.

  • Array add() добавляет элемент в локальный ArrayList.
  • Array addAll() вызывает локальный ArrayList для добавления каждого элемента.
  • ArrayCount add() вызывает свой родительский add(), а затем инкрементирует счётчик.
  • ArrayCount addAll() вызывает свой родительский addAll(), а затем инкрементирует счётчик по количеству элементов.

И всё работает замечательно. А теперь критическое изменение выделенной строки:

public void addAll(Object elements[])
{
    for (int i = 0; i < elements.length; ++i)
        add(elements[i]); // эта строка была изменена
}

Что касается владельца базового класса, то он работает так, как заявлено. И проходит все автотесты. Но владелец не обращает внимание на производный класс. И владелец производного класса сильно разочарован. Теперь ArrayCount addAll() вызывает родительский addAll(), который внутри себя вызывает add(), управление которым уже было ПЕРЕХВАЧЕНО производным классом. В результате счётчик инкрементируется при каждом вызове add() производного класса, а затем СНОВА инкрементируется по количеству элементов, добавляемых addAll() производного класса. ТО ЕСТЬ, ЭЛЕМЕНТЫ ПОДСЧИТЫВАЮТСЯ ДВАЖДЫ.

Раз такое может произойти, автор производного класса должен ЗНАТЬ, как был реализован базовый класс. И он должен быть информирован о каждом изменении в базовом классе, поскольку это может иметь непредсказуемое влияние на производный класс. Гхм! Эта огромная трещина будет всегда угрожать стабильности драгоценного Столпа Наследования.

Решение проблемы хрупкого базового класса


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

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

Проблема иерархии


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

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

Решение проблемы иерархии


А не так с ней то… что иерархия “общее/частное” не работает. Так для чего тогда хороши иерархии?

Для включения (Containment).

Если посмотреть на реальный мир, то вы везде будете натыкаться на иерархии включения. А чего вы не найдёте, так это иерархии “общее/частное”. Не будем пока на этом останавливаться. Объектно-ориентированная парадигма изначально была взята из реального мира, наполненного объектами. Но потом она начала использовать порочные модели, вроде иерархии “общее/частное”, аналогов которым в жизни не существует. Зато мир наполнен иерархиями включения. Хороший пример — ваши носки. Они лежат ящике, который находится внутри шкафа, который находится внутри вашей комнаты, которая находится внутри вашего дома и т.д.

Другой пример иерархий включения — папки в компьютере. Они содержат файлы. Как мы их категорируем? Если вспомнить о документах компании, то не особо-то и важно, куда их класть, в папку Документы или в папку Барахло. Важно то, что я категорирую их с помощью тэгов. Я помечаю файл такими тэгами:

  • Document
  • Company
  • Handbook

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

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




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

Проблема ссылки (The Reference Problem)


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

Решение проблемы ссылки


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

Полиморфизм, третий обрушившийся Столп




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

Нарушенные обещания




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

И что теперь?


Привет, функциональное программирование. Мне очень понравилось работать с тобой в последние годы. Чтобы вы знали: я не принимаю любые ваши обещания за чистую монету. Я сначала проверю их, чтобы поверить. Обжёгшись на молоке, будешь дуть и на воду. Ну вы поняли.
Mail.ru Group
Building the Internet

Comments 329

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

        Я — функциональщик. Прекрасно себя чувствую. Это вы от объектов такой раздражительный.
          +4
          А что функциональщики у нас не используют объекты и прочие чуждые для них элементы?
            +3
            «Новообращённые святее Папы Римского» ©
            Помнится недавно в обсуждении ФП, один из его адептов отказывался признавать LISP в качестве ФП (язык с которого ФП и началось), за то что в новых его версиях была добавлена поддержка ООП.
              +4
              за то что в новых его версиях была добавлена поддержка ООП.

              Интересно, насколько новых? Даже при чтении «Структура и интерпретация компьютерных программ» начиная с главы 3 идёт ООП. А книжка то старая.
                +2
                LISP появился раньше, чем C++ и последующая мода на ООП. LISP появился в 1954 году, C++ в 1983 году, а первая версия Object LISP в 1985 году. Однако, мода на ООП всех захлестнула после Windows 3.0, так как под DOS и UNIX без проблем обходились и без ООП, от чего был былинный срач о ненужности/нужности ООП, а вот Windows уже требовал именно ООП чтобы писать что-то неконсольное.

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

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

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

                    0

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

                      0

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

                        +3

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

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

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

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

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

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

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

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

                      Например?


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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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

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

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


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

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

                                          0
                                          Нее. Там бы всё и утонуло под весом «микро»сервисов)
                                          Вы тут совсем не в тему сказали.
                                      0
                                      Если точнее, то в команды процессора или байт-код, для java например.
                                  +3
                                  Проблемы, которые приписывают ООП это всё различные ипостаси проблемы протекающих абстракций. И эта проблема не имеет отношение к ООП, она касается любых инженерных продуктов. Например необходимость прогреть двигатель автомобиля зимой — протекающая абстракция. Пользователю хорошего продукта должно быть глубоко не интересно как устроен двигатель и какие особенности его эксплуатации в холодные дни. Он должен работать хорошо сам без необходимости дополнительных манипуляций со стороны пользователя.

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

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

                                  Подавляющая часть статьи это набор рассказов о плохой архитектуре, но не о проблемах ООП. Да и нет жёсткой границы в реальных ЯП — строго ООП и ни шагу за границу. ООП это один из многих инструментов и его можно использовать вместе с ФП, а не ФП вместо ООП. Может быть пора заканчивать войну и начать мирные переговоры?
                                    0
                                    Не хотите написать статью про дырки в ФП?
                                      0
                                      Вопрос не ко мне, но думаю, что это к вопросу ленивых вычислений.
                                      Скажем, банальная хвостовая рекурсия в Haskell спокойно выкушает всю память.

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

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

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

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

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


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

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

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

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

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

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

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

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

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

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

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


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

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

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

                                                        0
                                                        Добавлю ссылку на первоисточник цитаты из сообщения IIvana: http://lists.squeakfoundation.org/pipermail/squeak-dev/1998-October/017019.html
                                                        Полный текст интереснее.
                                                      +14
                                                      Проблема повторного использования вообще не касается ООП. В функциональном программировании при использовании чужого кода тоже надо знать, как он работает. И да, в следующей версии он может поменяться и все сломать.
                                                        –6
                                                        Проблема повторного использования — единственная проблема в программировании (речь не только о чужом коде, но и о своем). И да, в ФП тоже меняются интерфейсы, но происходит это значительно реже, так как меняется лишь одна функция. Если же в ООП меняется один метод или публичное свойство, то все остальные тоже идут на свалку, так как меняется интерфейс всего объекта. Мало того, если меняется интерфейс любого из родителей, то на свалку идет вся эта гора. Грубо говоря, ФП позволяет делать детали максимально мелкими. Это добавляет сложности композиции, но это уже к вопросу об умственных способностях программистов. Собственно, нет вопроса, что эффективнее, есть вопрос — где взять столько умов. Поэтому, простые языки, типа Go, в промышленности эффективнее, гораздо проще менять разработчиков.
                                                          +8
                                                          Проблема повторного использования — единственная проблема в программировании

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

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

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


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

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


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

                                                              Почему это?

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

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


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

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


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

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

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

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


                                                                      Именно так.

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


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

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

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

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


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

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


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

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


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

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

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

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


                                                                              PS


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

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


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

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

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

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

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

                                                                                                > IT-директор

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

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

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

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

                                                                            Извините за личный вопрос, Вы случайно не закончили краткие курсы, вместо учёбы в ВУЗе?
                                                                            Уж очень в Вашем сообщении сквозит невежество и отсутствие какого-либо знакомства с теорией.
                                                                          +3
                                                                          А нет никакой сложности в написании линейной программы

                                                                          Про "проблему остановки" вы похоже не слышали.

                                                                            +4
                                                                            Да что Вы, у человека вообще сложностей в программировании нет совершенно никаких. Вся индустрия баклуши бьет.
                                                                              –3
                                                                              Это не проблема описания алгоритма, это проблема самого алгоритма. Программирование занимается переводом одних языков на компьютерные. И все. Программирование — функция, на вход которой поступает готовый алгоритм, а задача описать его компьютеру. И вся сложность заключается в том, чтобы не описывать одни и те же алгоритмы многократно. Соответственно, нас интересуют только те теории, которые позволяют сократить код (например, теория категорий). Однако, хоть эта задача и относится к единственной сложности в программировании, даже ее решают математики, а программисты лишь используют частично и подгоняют под себя.
                                                                              Все по одной простой причине, мы всегда пишем программу для конкретного исполнителя (для которого уже задан набор команд), будь то процессор или виртуальная машина.
                                                                              А когда пишут псевдокод, просто в голове подразумевают ряд существующих языков (написанных для конкретного процессора) и пишут нечто, для чего можно в любой момент написать компилятор.
                                                                                +1
                                                                                Я с теорией категорий немножко знаком, и мне непонятно, как она помогает писать более короткий код.

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


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

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

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

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


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

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


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

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

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

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

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

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

                                                                                    Согласен, все это путать не надо, да и в википедии (по вашей же ссылке) примерно это и описано 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. А на требование «дай мне кнопку сделать все» дают код в ответ не программисты, а несуществующие персонажи из сказки «Программист — бог».
                                                                              +10
                                                                              либо проблем две, и это именование и инвалидация кэша.
                                                                              две: именование, инвалидация кеша и ошибки на единицу.
                                                                                0

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

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

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

                                                                                  +7

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

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

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

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

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

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

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

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

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

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


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


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


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

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

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

                                                                                                                    в Java как-то не очевиднее.
                                                                                                                      +1
                                                                                                                      Потому что там на самом деле три метода: публичные add и addMuti и приватный/протектед addImpl, при этом первые два никогда не вызывают друг друга, а делегируют непосредственную вставку третьему.

                                                                                                                      Хочешь перехватывать общение с пользователем класса — переопределяешь первые два. Хочешь перехватывать реализацию — переопределяешь третий.

                                                                                                                      Non-virtual interface из плюсов тут, кстати, тоже где-то рядом.
                                                                                                                    +14
                                                                                                                    Как же надоели такие статьи! Человек работал 10 лет с ООП, но так и не научился его готовить? И вместо того чтобы понять когда что использовать он предлагает всё переписать на ФП. Набьёт шишек там и напишет точно такую же обличительную статью про ФП. Прям круговорот какой-то…
                                                                                                                    +3
                                                                                                                    Только недавно перечитывал Страуструпа и там один из вариантов решения «проблемы ромба» на С++:
                                                                                                                    class Copier: public Scanner, public Printer {
                                                                                                                    public:
                                                                                                                    using Printer::start;
                                                                                                                    }
                                                                                                                    


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

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

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

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

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

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


                                                                                                                              class Copier : Printer , Scanner {
                                                                                                                              
                                                                                                                                  /// запускает основную функцию девайса (большая зелёная кнопка)
                                                                                                                                  start(){
                                                                                                                                      return super->Printer::start( super->Scanner::start() )
                                                                                                                                  }
                                                                                                                              
                                                                                                                                  /// конкретно у копира есть дополнительная кнопка запуска отдельно принтера
                                                                                                                                  print() {
                                                                                                                                      return super->Printer::start()
                                                                                                                                  }
                                                                                                                              
                                                                                                                                  /// конкретно у копира есть дополнительная кнопка запуска отдельно сканнера
                                                                                                                                  scan() {
                                                                                                                                      return super->Scanner::start()
                                                                                                                                  }
                                                                                                                              
                                                                                                                              }
                                                                                                                                +2
                                                                                                                                Для начала нужны PrinterInterface и ScannerInterface.
                                                                                                                                Тогда все становится понятно: Copier implements PrinterInterface, ScannerInterface.
                                                                                                                                Если в обоих интерфейсах методы называются start, а не scan и print, то будет плохо, но это надо было сразу думать над неймингом.
                                                                                                                                Если же методы называются print() и scan(), как у нормальных людей, то и проблем нет.

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

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


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

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

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


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

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

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

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

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

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


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

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


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

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


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

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


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

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

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

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

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

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


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

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


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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

                                                                                                                                      Если у вас реализации интерфейсов не пересекаются, что дальше то? Кастим к одному из интерфейсов и получем обратно принтер, от которого наследовались?
                                                                                                                                        0

                                                                                                                                        А что вы дальше ожидаете от МФУ?

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

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

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

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

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

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

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

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

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


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

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

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

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


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

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

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

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

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

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


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

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

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

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

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

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

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

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


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


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

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

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

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

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

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

                                                                                                                                                                  Ох…

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

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


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


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

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

                                                                                                                                                                      — Доктор, когда я вот вот так вот делаю у меня болит..((
                                                                                                                                                                      — А вы вот вот так вот не делайте
                                                                                                                                                                –1
                                                                                                                                                                Суть наследования в расширении базового класса. Наследуем то, что есть и добавляем то, чего нет. Всё прекрасно делается без классов. Есть нода-контейнер. Есть ноды с контентом. В контейнере можем оверрайдить контенты хоть до опупения. Причём, заметьте никаких жестких связей. ООП построенное на классах в топку! Это по любасу приводит к росту статического ядра.
                                                                                                                                                                  0
                                                                                                                                                                  babylon может статью с подробными пояснениями про нода-контейнеры напишете? *стало любопытно*
                                                                                                                                                                    –1
                                                                                                                                                                    Для статьи слишком мало материала. Нода-контейнер любая нода, лежащая в массиве неймспейсов to
                                                                                                                                                                      0
                                                                                                                                                                      Если начать с введения, что такое Нода итп, то на статью вполне потянет.
                                                                                                                                                                        –1
                                                                                                                                                                        Idot Нода — наименьшая структурная единица. Родительские ноды лежат в массиве неймспейсов parent. Агрегирование (сборка) структуры происходит снизу вверх по индексам ключей. Работа сверху вниз от корня или от контекста, который заранее установлен. Контейнеры лежат в массиве неймспейсов from. Контенты — в to. Сигналы в массиве неймспейсов signals. Меньше параграфа.

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

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

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

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

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

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

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

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

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

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

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

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

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

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