Епічна битва ФП проти ООП
Неділя, 5 Серпень 2018 16:46![[personal profile]](https://www.dreamwidth.org/img/silk/identity/user.png)
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 - это и в самом деле просто.
Но вы попробуйте - без гугления, что это такое. Вообще, предполагается, что человек, говорящий «ФП это круто» должен знать ответ на этот вопрос. А не просто нихуя не понимать в ООП.
Самая поразительная штука, которая творится у нас в хайтеке, (если так вообще можно назвать программизм головного мозга), это то, что он давно уже никакой не хайтек.
Ну, то есть, как. Есть, конечно 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 - это и в самом деле просто.
Но вы попробуйте - без гугления, что это такое. Вообще, предполагается, что человек, говорящий «ФП это круто» должен знать ответ на этот вопрос. А не просто нихуя не понимать в ООП.
...
Дата: Понеділок, 6 Серпень 2018 13:07 (UTC)Выход: довести интервьювера до белого каления вопросами, огораживающими периметр задачи бетонным забором.