Комментарии 87
Но на JS плохой код писать проще, чем на языках с ООП.
Такое замечание сильно дискредитирует статью.
Тут скорее имелось в виду, что из-за мультипарадигменности и "свободы" JS на нём можно писать многими способами. Сам язык не даёт каких-то подсказок на эту тему. Языки же с более строгим "вектором" конечно ограничивают разработчика, но и дают ему многое. Статический анализ, типизация и прочее. Учитывая популярнось TS, Flow, уже очевидно, что мировое сообщество заинтересовано в типизации в web
Я тоже считаю что как-то неправильно не использовать функциональные (или в целом даже мульти) парадигмы. И тем более необосновано заявлять что JS отстой, а ООП серебряная пуля.
Как будто в ООП нельзя лишних singlton-ов наделать, и сильную связность по всему проекту и так далее.
Мне почему-то кажется уже несколько лет что ООП свое отживает. С выпуском многоядерных процессоров для широкой общественности всех ООП фанатов рынок заставит обрить голову и идти в сторону всяких окамлов и лиспов. Есть правда вариант что они продолжат катить квадратное, но подох же джамп в конце концов, авось и объекты туда же...
С чего бы вдруг? Функциональное программирование не даёт никаких волшебных преимуществ в распараллеливании.
Правда, не дает. Просто ООП дает волшебные проблемы с распараллеливанием, а поскольку в ФП их нет...
Какие такие проблемы у ООП с распараллеливанием?
Но я начал активно использовать ФП подход в ООП языках только после того как прошел семь кругов ООП «паттернов» и «дизайна», так вот — чем меньше ООП в моих программах сегодня — тем меньше багов, абсолютно линейная зависимость. С ФП также можно перемудрить, но он больше прощает благодаря иммутабельности.
А ООП — это паттерн для обмена сообщениями между *переменными*, соответственно мутабельность идет первым классом, и все книги написаны о том как лучше менять переменные, а не о том как этого не делать.
Многое ниже будет очевидно.
Верно, неправильно применяя ООП, можно добиться невероятных приключений в разработке и поддержке кода.
На самом деле, ООП — это наследование, полиморфизм и инкапсуляция.
Если по простому — ООП это паттерн, в котором у функций есть первый аргумент, олицетворяющий this, (self, не важно), некий объект, являющийся логическим центром обработки. Эта функция не обязана менять состояние объекта, она может быть и const и pure.
Если в вашем ФП коде есть методы аля updateUser и removeUser, которые принимают первым аргументом один и тот же тип объекта — это ООП.
Вот так просто, есть объекты и их методы. Все остальное — не ОО функции.
Конечно, мало просто иметь функции с одинаковым первым аргументом, это еще не полноценное ООП.
1) Наследование. Единственное требование для наследования — возможность статически переопределять метод объекта, порождая новый тип так, чтобы в новом типе работали все предыдущие методы объекта.
Это значит, что ваш updateUser и removeUser не ОО, потому что нельзя как-то создать новый тип User2, чтобы с ним updateUser вел себя иначе. Функции должны вызываться от типа. Как видите, это не связано с shared state, это лишь паттер — кладите функции внутрь типа, чтобы при наследовании типов одно и то же имя функции вело себя по разному.
2) Полиморфизм. Это про то, что одно и то же имя функции будет вызывать разные функции для объектов, чей тип заранее _неизвестен_. Это опять же не связано с shared state, это ровно про то, что — у вас есть интерфейс, и разные типы могут реализовывать его по разному.
3) Инкапсуляция. Это вообще и про ФП и про ОО. Есть ряд данных и функций, которые мы хотим скрыть от публичного интерфейса. Повесить на них ярлык — эти вещи не для внешнего использования, эти функции можно использовать только внутри определенных функций. Классический пример — функции, начинающиеся с _ или __
__updateUser — скрытая функция, предназначенная для системы работы с пользователями, но никакая другая система не вправе вызывать эту функцию.
updateUser — публичная функция, предназначенная для вызова из любой системы.
Так или иначе, в любом — ФП, процедурном стиле — мы используем ООП, не замечая этого. ООП не про объекты — ООП про абстракцию в виде объектов, их методов и того как эти методы должны работать, переопределяться и быть доступны\недоступны в этом контексте.
ООП — изменяемые переменные и повторное использование кода путем наследования, с жесткой привязкой кода к типу данных
ФП — неизменяемые переменные и повторное использование кода путем передачи функций через указатель, а степень привязки кода к типу данных зависит от языка
Инкапсуляцию оставим за бортом, так как это свойство конкретного языка программирования, а не парадигмы. В С инкапсуляция ни чуть не хуже чем в С++.
В части многопоточности ФП впереди так как держит изменение переменных под контролем, что делает каждый рейс кондишен очевидным в момент когда программист его пишет. Т.е. в ФП программисту нужно запариться чтобы сделать рейс кондишен.
В части переиспользования кода ФП также впереди, так как не полагается на бурную фантазию разработчика, лепящего абстракции и «оживляя» данные, а позволяет просто переиспользовать код когда нужно.
С ФП у нас есть только одна реальная проблема — множество теоретиков от математики набежало и сделало ФП трудным для изучения. Хотя основы ФП просты как палка, начинающим упорно советуют начать обучение с языков-головоломок вместо каких-то практичных вещей.
В то время как по ООП есть отличные книги — помню лет 15 назад дал человеку «С++ для чайников», так он за пол года написал приложение для сбора статистики с нефтяных скважин, со своим драйвером модема, с графиками, базой данных и девицами. При полном отсутствии интернета в качестве источника информации.
С ФП у нас есть только одна реальная проблема — множество теоретиков от математики набежало и сделало ФП трудным для изучения. Хотя основы ФП просты как палка, начинающим упорно советуют начать обучение с языков-головоломок вместо каких-то практичных вещей.Вот прям в яблочко! Аж обидно от этого становится… А объяснить без этой базы (теоркат например) что-то более менее высокоуровневое сложновато, потому как шаг влево с тропинки, и все, джунгли из монад.
Т.е. в ФП программисту нужно запариться чтобы сделать рейс кондишен.
В современных ООП языках тоже нужно париться для этого.
«ООП — изменяемые переменные»
Вовсе нет. ООП не требует изменяемых переменных, только инкапсуляцию, наследование и полиморфизм.
Простите, если неверно подал мысль, я не говорил что ФП = ООП, я говорил что ООП и ФП это паттерны разных категорий, и они могут существовать вместе, а значит не противопоставляются.
Но ООП — это по определению именно изменяемое состояние. Если объект при вызове метода вместо изменения состояния возвращает свою измененную копию, то это уже не ООП — это поведение дата-класса в лучшем случае (считается анти-паттерном проповедниками ООП, они даже придумали унизительное название «анемичная модель данных»). А в худшем случае можно сказать что это просто функция которая в лучших традициях ФП возвращает новое значение. При этом взаимосвязи в между объектами теряются и они не могут передавать друг другу сообщения, только от владельца обладаемому.
ООП по определению — это именно изменяемое состояние, это симуляция реальных объектов путем обмена сообщениями — см. причины создания первого ООП языка — Симула. Каждый объект представлен именно как объект, по одному объекту на симулируемый объект, и ни как иначе. Никакого копирования объектов для обеспечения иммутабельности там не было — это недавняя придумка программистов, которых окончательно выбесило дикое количество багов в ООП программах и которые решили хоть как-то упорядочить этот хаос. Ограничения по памяти и примитивная сборка мусора также не давали осуществить стратегию иммутабельности.
Инкапсуляция, наследование и полиморфизм — это вовсе не определение ООП, это характерные особенности некоторых реализаций ООП, при том якобы эксклюзивная фишка «наследование» реализовано даже не во всех ООП языках. Само наследование есть только в ООП основанном на классах, можно сказать что это не ООП, а Класс-Ориентированное программирование. ТЛДР — Смысла в этом трио я вообще не вижу, им здорово только студентов пугать.
«Но ООП — это по определению именно изменяемое состояние.»
Нет же. ООП не требует изменяемого состояния. Методы объекты могут возвращать что им угодно.
"(считается анти-паттерном проповедниками ООП, они даже придумали унизительное название «анемичная модель данных»)."
Я не знаю этих людей, и почему они называют себя проповедниками ООП, но поверьте, к ООП эта идея не имеет никакого отношения.
«ООП по определению — это именно изменяемое состояние, это симуляция реальных объектов путем обмена сообщениями»
Вот именно с этим заблуждением и боролся мой первый комментарий.
из вики
«Объе́ктно-ориенти́рованное программи́рование (ООП) — методология программирования, основанная на представлении программы в виде совокупности объектов, каждый из которых является экземпляром определенного класса[1], а классы образуют иерархию наследования[2][3].»
В определении все хорошо — от программы требуют писать все в объектах, их методах, взаимосвязь типов обязуют делать через наследование.
Есть и другое определение, что ООП — это наследование, инкапсуляция и полиморфизм.
Ни в одном из определений вы никогда не встретите требования иметь изменяемые данные.
В википедии, оказывается, все давно написано! :-D
Наивно.
Формально точного определения ООП не существует, и да простите меня за новую цитату с ненадежной вики, но непосредственно ключевые, необходимые и достаточные моменты для ООП известны и точны:
«Ключевые черты ООП хорошо известны:
Первая — инкапсуляция — это определение классов — пользовательских типов данных, объединяющих своё содержимое в единый тип и реализующих некоторые операции или методы над ним. Классы обычно являются основой модульности, инкапсуляции и абстракции данных в языках ООП.
Вторая ключевая черта, — наследование — способ определения нового типа, когда новый тип наследует элементы (свойства и методы) существующего, модифицируя или расширяя их. Это способствует выражению специализации и генерализации.
Третья черта, известная как полиморфизм, позволяет единообразно ссылаться на объекты различных классов (обычно внутри некоторой иерархии). Это делает классы ещё удобнее и облегчает расширение и поддержку программ, основанных на них.
Инкапсуляция, наследование и полиморфизм — фундаментальные свойства, которыми должен обладать язык, претендующий называться объектно-ориентированным (языки, не имеющие наследования и полиморфизма, но имеющие только классы, обычно называются основанными на классах).»
И я с этими ключевыми моментами абсолютно согласен. И категорически не согласен, что изменяемые данные — это свойство или необходимый ключевой момент для ООП.
Нравится разбираться и искать истину. Дух сообщения не в инкапсуляции же был, а в том, что она — действительно ключевой момент ООП, а shared state — вообще не момент из ООП, и никак к нему не относится.
Она перестанет быть ОО, если в паблик попадут скрытые методы и данные, т. к. в процессе написания кода разработчик справедливо будет использовать публичные функции и данные, которые для этого не предназначены, и полезут UB.
Она перестанет быть ОО, если в паблик попадут скрытые методы и данные
Не перестанет.
Сколько можно путать сокрытие и инкапсуляцию...
И нет, ошибочный ООП код не перестаёт быть ООП.
Следовательно, инкапсуляция не является *обязательным* свойством ООП.
Поедем дальше. Что если я заменю все наследование в ООП коде на композицию, согласно с современным best practices — «favor composition over inheritance»? Вы узнаете ООП и в этом случае, не так ли?
Но еще остался полиморфизм. Мы уже удалили все наследование, соответственно в небольшом количестве мест где нам действительно нужен полиморфизм, мы использовали интерфейсы. Интерфейс — это всего лишь таблица с функциями, не так ли? Можно ли назвать любое место где используются таблицы с функциями — ООП? Конечно нет.
Но после всех этих трансформаций вы все еще можете распознать ООП в оригинальном приложении, не так ли? Вот объект Окно, у него есть положение, оно может передавать сообщение о том что пользователь нажал крестик другим объектам… По каким признакам вы узнаете ООП? Эти признаки и есть *настоящее определение ООП*, а вовсе не эти три пункта, которые вы копипастите. ;)
Настоящее определение ООП — это представление предметной области в виде групп переменных и функций, и эти группы (объекты) взаимодействуют путем отправки друг другу сообщений (вызывают функции друг друга).
А вот объединение функций в группы — и есть инкапсуляция. В любом коде с объектом и методами будет инкапсуляция.
Предпочитать композицию хорошо, но что делать, если уже есть компонент VelocityComponent, и нам нужен (правда правда, мы в игре совершенствуем физику, и нам нигде не нужен VelocityComponent, зато нужен будет SuperVelocityComponent. И вот здесь, в терминах композиции я буду вынужден с нуля описывать SuperVelocityComponent, или как-то хитрить, и делать объект, у которого VelocityComponent это поле, и он просто проксирует через свои методы все все методы этого компонента, но суть будет все равно в том, что для правильной архитектуры нам будет нужно наследование для такой операции. Вы можете искуственно ограничить себя и сказать — без наследования все равно ООП но мне правда кажется что нет. То есть вот она, программа — код написан, и он работает, но именно из за отсутствия возможности расширять код дальше через наследование — убивает для меня объектную природу этого кода. Она становится компонентной, если позволите.
Полиморфизм. Интерфейсы. Можно ли назвать таблицу с функциями полиморфизмом? Нет, ведь таблицы бывают разные, но та таблица с функциями, которая в зависимости от типа отдает разные функции — реализация полиморфизма с одним аргументом.
представление предметной области в виде групп переменных и функций
Это и есть суть инкапсуляции, а не "модификаторы private".
Что за ООП язык без инкапсуляции?
Там всё в порядке с инкапсуляцией.
Но вообще-то, поддержка инкапсуляции на уровне компилятора совершенно не обязательна. Достаточно code style нотации — к примеру ставить __ перед приватными членами и методами.
Полиморфизм. Это про то, что одно и то же имя функции будет вызывать разные функции для объектов, чей тип заранее неизвестен.
Это динамическая диспетчеризация. Полиморфизм — возможность передавать аргументы разных типов.
Инкапсуляция. Это вообще и про ФП и про ОО. Есть ряд данных и функций, которые мы хотим скрыть от публичного интерфейса.
Это сокрытие. Инкапсуляция — объединение данных и функций для работы с ними в одной капсуле — объекте.
Вас кто-то заставляет шарить мутабельное состояние?.
У вас, видимо, неправильный ООП
Посмотрел лекцию. Аргументация хромает. Почему, к примеру, getPath нельзя, а getContent можно? Статические функции — зло? Я знаю почему *иногда* это зло (если статическая функция меняет внешние переменные), но аргументация в лекции основана на «это устарело». Мы где-то это слышали — 3.5мм джек устарел. Поэтому мы будем использовать переходники, это современно и элегантно (см «Элегантные объекты»).
Мы будем благодарны за вопросы в комментариях и, если потребуется, напишем более подробные технические статьи о работе с Dart на их основе.
Хотелось бы узнать подробнее про серверную часть.
Что нужно для того что бы запустить на сервере код который написан на Dart?
Можно ли написать на Dart, полноценное API к приложению?
Давайте по-порядку:
что представляет из себя компонент, который вы зарелизели
У нас есть такая штука, как одновременное редактирование задач. Как в google документах, видно кто и что сейчас пишет, есть маркеры и пр. В основе лежит парадигма Operation Transformation. Как раз backend часть этой фичи и написана на Dart. Вкратце приложение общается с MongoDB и Postgress и с клиентом по websockets и передаёт туда-обратно данные
вы полностью его переписали на Dart или он по прежнему гибридный
Код в основном на Dart, есть небольшие модули на node, но в скором времени и их не будет
Что нужно для того что бы запустить на сервере код который написан на Dart?
Поставить Dart
Можно ли написать на Dart, полноценное API к приложению?
Не очень понимаю, к какому, но да, ничто не мешает это сделать. Есть такая штука Aqueduct
Ну и так далее. Я к чему — все это не показатель. То есть, мы рады за вас то у вас все полуается, но…
Function0 — функция арности 0 (количество аргументов = 0).
Аналог Supplier в java 8. Сокращенная запись () => T, но можно записать и как Function0[T] (не ясно, правда, зачем). https://scalafiddle.io/sf/qs25LOB/1:
def test1(f: () => String) = f()
def test2(f: Function0[String]) = f()
println(test1{ () => "Hi!" })
println(test2{ () => "Hi!" })
P.S. Увы, сегодня в 11 вечера по Москве отбываю на дачу. Появлюсь не раньше чем через неделю.
Просто scala.js отлично подходит тем, кто уже залез в глубины scala и не готов расставаться. А вот для изучения… безумству храбрых поем мы песню =).
Преимущества scala.js проявиться могут либо в единой кодовой базе с BE на jvm, либо в хардкорном метапрограммировании, либо в не менее хардкорном FP с вложенными монадами и прочими прелестями. Еще опция — формализация вашей задачи и описание в виде DSL.
Для всего этого надо быть scala спецом, решившим покодить под FE, а не наоборот.
А вот что с нуля вам предложить как киллерфичу scala.js я даже не представляю.
Большое вам спасибо! Все не знал с какой стороны подступиться :)
А мне наоборот такие книги чудесно заходят.
Learn You Some Erlang так вообще прекраснейшая вещь. Про Хаскель тоже.
Например сверстать динамическую веб-страничку для своего сайта. И сделайте это на scala.js
Ну не было под хаскель никакого реального проекта!
Попробуйте провернуть то же самое с purescript. А тут книжка.
Лично я не настолько хорошо знаком с разработкой FE, чтоб писать такие статьи.
Может сразу и Kotlin.js захватите?
Два года с Dart: о том, как мы пишем на языке, который ежегодно «хоронят» (часть 2)