bytebuster: (ITCrowd-Cartoon)
...Вона блює і співає ([personal profile] bytebuster) wrote2018-08-05 04:46 pm

Епічна битва ФП проти ООП

Vlad Balin: #ООП #чтоэтозахрень #говнокод #хайтек

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

Ну, то есть, как. Есть, конечно Computer Science - это серьезные дяди-теоретики (и тети - например Барбара Лисков) из университетов, которые пишут статьи, книги, и диссертации. Но книги и статьи получаются слишком сложные, так что типичный говнокодер их не читает.

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

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

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

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

> А че там? Я читал про ключевое слово class, я знаю ООП, там все просто, и интуитивно понятно.

Сейчас проверим. Есть у тебя два сту^D^D^D класса. Один - "Квадрат". Другой - "Прямоугольник". Рисуем их - без поворотов, стороны вертикальны и горизонтальны. Какое утверждение из перечисленных верно? Объяснить почему:

А) Квадрат наследуется от Прямоугольника
Б) Прямоугольник наследуется от Квадрата
В) Их нельзя наследовать друг от друга.


У меня большой опыт с этой задачей - я задавал ее на собеседовании каждому программисту, и это за последние двадцать лет, наверное, больше сотни человек. Вообще-то, правильный ответ В. Это вариант Ellipse-Circle Problem, представляющей собой известный пример нарушения Liskov Substitution Principle. Никто ни разу не давал на этот вопрос правильный ответ. Это и не требовалось - наибольший интерес представляет "объясните почему", и следующая за этим дискуссия о природе ОО. Вот тут кошмарная бездна "понимания ООП" открывается. Причем, совершенно не важно, говорит человек, что он знаком с LSP, или нет.

Трое из четверых (и это поразительно), сходу говорят "Б". Почему?

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

Задумчивость. Человек понимает, что началось что-то не то, и почва явно уходит из под ног. Ну окей.

- Тогда А - Квадрат от Прямоугольника!
- Почему?
- Ну, множество квадратов - это подмножество прямоугольников. Квадрат - это прямоугольник, но не каждый прямоугольник - это квадрат.

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

- Теперь я добавляю в прямоугольник метод - растянуть его по оси о-икс. rect.stretch( a ), где a - коэффициент, во сколько раз растянуть. Могу я добавить такой метод в прямоугольник?
- Да, конечно, нет проблем.
- Каким именно образом этот метод будет вести себя в квадрате?

Шок. Ступор.

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

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

Далее, тем кто добрался до В, мы задаем контрольный вопрос. Сугубо теоретический. "Круто, еще одна задачка. У нас есть действительные и комплексные числа. Что от чего наследуется?"

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

Есть очень небольшая категория людей (единицы в моей практике собеседований), которая быстро понимает, что проблема вызвана "замкнутостью" методов (метод не может выводить объект из его класса), и предлагает решение. Сделать метод stretch() immutable - пусть он не меняет состояние класса, а всегда создает вместо этого новый прямоугольник. И все.

Это отличное решение. Здесь - поможет, но в общем случае с ним есть нюанс - мы далеко не всегда можем воспользоваться этим фокусом. До первой кольцевой ссылки, или момента, когда мы сталкиваемся с вводом-выводом - например, часть состояния объекта это дескриптор какого-то ресурса, который нельзя так запросто копировать.

И вот тут часть людей говорят:

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

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

Думать об объектах в терминах их составных частей - естественно, и интуитивно понятно. И это совершенно не удивительно, что мало кто может решить эту задачу старушки Лисков на "правильное наследование" (на самом деле - определение "strong behavioral subtyping"). Думать об объектах как об абстракциях, определяемых поведением, игнорируя их структуру - напротив, неестественно, и требует не просто осведомленности и тренировки, а полного разворота сознания.

Но самое интересное не это. Основная проблема, которую эффективно решает ОО (и ничего лучше для нее человечество пока не придумало) - это декомпозиции большого и сложного состояния. Ну, то есть, то самое, стонами о чем сообщество фронтенда наполнено чуть менее чем целиком - у них в простеньких страничках "state management is hard". Как же ООП помогает? Сейчас объясню, это просто.

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

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

- А что не говно?
- ФП!
- Неужели?
- Да. Гораздо проще, чем это ваше говенное ОО.
- Почему же?
- Ну, там все immutable, все на чистых функциях, а потому просто, и понятно.

Тут человек получают задачу сделать immutable FIFO Queue, со средней сложностью операций O(1). ФП - это же просто, ну. Ну что может быть проще? Но это уже другая история.

ЗЫ: Okasaki Queue - это и в самом деле просто.
Но вы попробуйте - без гугления, что это такое. Вообще, предполагается, что человек, говорящий «ФП это круто» должен знать ответ на этот вопрос. А не просто нихуя не понимать в ООП.
juan_gandhi: (Default)

[personal profile] juan_gandhi 2018-08-05 03:46 pm (UTC)(link)
Кстати, откуда все это поклонение перед Лисков? Во-первых, она там что-то напутала...
juan_gandhi: (Default)

[personal profile] juan_gandhi 2018-08-05 06:22 pm (UTC)(link)
Ну польза-то есть, конечно. Но там не вполне научно.

ООП - це минуле сторіччя :)

[personal profile] hutorny 2018-08-05 04:29 pm (UTC)(link)
ukurainajin: (Default)

[personal profile] ukurainajin 2018-08-05 05:09 pm (UTC)(link)
Власне, я згоден, що усе залежить від методів (тобто можливостей), які ми хочемо. Ми ж не знаємо, що це за прямокутник із квадратом. Досвід підказує, що треба на самому початку створювати абстрактний клас фігури, керованої двома вимірами, і це, по своїй суті, буде прямокутник. Із квадратом не буде проблем, він залишатиметься завжди квадратом, бо заміщення методів (чи у рамах цієї розповіді таке вважається говноприйомом, я не розумію?) А от якщо вимоги такі хитрі, що не можна чіпати вимір, про який не питали явно, тоді, звісно, це функціональне протиріччя, треба його розв'язувати із замовником (бо що тут взагалі робить цей квадрат)
ukurainajin: (Default)

[personal profile] ukurainajin 2018-08-05 05:23 pm (UTC)(link)
Якщо вже дивитися ширше, то я як системний аналітик взагалі не можу сказати заздалегідь, як це буде зреалізовано, доки не знатиму, як воно має використовуватися. :)
tiresome_cat: (Default)

[personal profile] tiresome_cat 2018-08-05 09:48 pm (UTC)(link)
Помнится был у меня случай, когда пришлось адаптировать кое-какие модули на С++ для интеграции в 1С. Причем в ООП я разбираюсь как свыня в апельсинах. "Программирование не приходя в сознание". Да :(
tiresome_cat: (HappyCat)

[personal profile] tiresome_cat 2018-08-06 07:42 am (UTC)(link)
Там бьіл затьік со скоростью обработки внешних данньіх, поетому 1С-овский внешний отчет не канал. Пришлось писать обработчик на С++.

[personal profile] cross_join 2018-08-06 01:07 pm (UTC)(link)
Описан типичный способ игры "Почему бы вам не...? Да, но...".
Выход: довести интервьювера до белого каления вопросами, огораживающими периметр задачи бетонным забором.