> не влил достаточного количества ресурсов, чтобы сделать из него конфетку
Зачем? Почему не взять html+js и не написать нормальный фронтенд без свистоплясок и трансформаций?
js+html выучить в разы легче, чем ту же java, думаю, js+html выучить легче, чем нормально разобраться в GWT =)
А если так — зачем использовать GWT и вливать в него ресурсы?
В данном случае это java. Самая большая глупость, которую можно сделать — это генерировать js из java кода. Это кажется хорошей идеей, т.к. позволяет изучить только один язык и развиваться в нем.
На самом же деле — это лишь вводит ограничения в проект. Фронтенд уходит далеко вперед, а технологии, которые занимаются извращением — не могут поспевать за этим всем.
> Какие особенности синтаксиса и семантики языка делают его пригодным для написания кода на фронтэнде, а какие — на бэкэнде?
Если мы говорим про web, а мы говорим про него, то ответ очевиден: Главная особенность фронтенд языка — он поддерживается браузерами без плагинов.
> почему GWT — это плохая технология?
Мой позиция в том, что бекенд (в виде java) не должен заниматься рендером html, и уж тем более не должен заниматься кодогенерацией js. Есть конечно случаи, когда без серверного рендера не обойтись, но это можно сделать и без GWT. А вот генерировать js — это и вовсе плохая идея, по-моему, и ни одной адекватной причины для этого не вижу.
У проектов, где бекенд занимается рендером страниц нету возможности расширяться в сторону поддержки мобильных девайсов (или эта поддержка требует много усилий).
Количество боли (время, нервы, количество выпитого кофе), которую приходится испытывать при работе с технологией, по-моему, хороший показатель для этого.
Конечно это все субъективно, кому-то нравится, кому-то нет.
Ох, а ведь казалось, что такой классный язык получиться должен был. Ведь разрабатывали очень крутые ребята. А вышло так себе. Взяли груви и немного приукрасили. Обидно.
Да, вы правы, типы не делают язык инженерным. А так же типы есть не только в ооп.
Ну и типы действительно делают язык более сложным.
Сложным становится потому, что:
1. движки должны переписаться для поддержки типов, думаю, на данный момент это сложно сделать;
2. разработчикам вместо того, чтобы закинуть просто объект, нужно будет сделать инстанс типа, а это больше кода и в консоли браузера тяжелее тестировать, и вообще «верните мне мой js» (когда-то был крик души на хабре про появление Angular в мире js).
Если быть честными, то GWT — это плохая технология, как JSF и куча других решений, которые предлагают на бекенд-языке писать под фронтенд.
Да, когда-то это было обосновано и, возможно, даже правильно. Но вот уже лет 5-10 как, это все стоит выкинуть на свалку истории, но до сих пор люди мучаются с поддержкой этого легаси.
Так ведь сам TypeScript — это сомнительная поделка.
Да, есть удобство от типизиации, но в целом спроектирован TS криво.
Сейчас они вроде перешли на более-менее нормальный импорт зависимостей (из ES2015), а раньше у них импорт описывался через комментарии с xml тегом.
Да и в целом, они решили сделать велосипед, в то время, когда их нужды без проблем мог покрыть ActionScript3. Который уже на тот момент имел поддержку во многих IDE, который был известен очень большому количеству людей и был очень даже популярным.
Да, оно то похоже, но все же есть пара замечаний.
Паттерны «стратегия», если простыми словами, — это когда аргумент функции (метода) принимает на вход «кусок кода» (функцию или имплементацию стратегии в ООП), который будет выполняться в работе данной функции.
Т.е. когда есть конкретная функция, которая вызывает другие конкретные функции — это не будет стратегией.
Функция, которая вызывает другую функцию для получения третьей (к примеру керринг) — это не будет стратегией.
А вот если в функцию передается другая функция — это уже будет стратегией.
> асинхронная обработка с continuation-passing
Это больше похоже на стратегию. Вызывается асинхронный метод, и в него передается callback функция, в которую будет выполнен возврат после выполнения асинхронного кода.
Chain of responsibility — это механизм, лежащий в основе pub/sub.
Dispatcher (на него подписываются и через него кидают event) внутри содержит хеш-таблицу сообщение -> список слушателей. Когда кто-то публикует сообщение, диспатчер берет список слушателей и поочередно выполняет их.
(но тут я не прав: «chain of responsibility по учебнику» будет заходить в каждую функцию и говорить «пришло такое сообщение», функция же должна будет посмотреть на это сообщение и решить выполнять над ним логику или нет)
UnsupportedOperation — это первый признак нарушения LSP.
А так же, в сигнатуре метода не стоит объявлять конкретный класс, т.к. дальше этот метод не сможет быть переиспользован с другими стримами. Тут нужен композитный интерфейс, который наследуется от обоих, и все имплементации должны идти от композитного.
После знакомства с TypeScript я начал сомневаться в адекватности его авторов. Они взяли js добавили к нему типизацию, но сделали это криво. Сделали это через велосипед и костыли. А ведь все уже было готово, готовый язык, готовые ИДЕ, статические анализаторы и прочее. Нужно было лишь взять и дописать транслятор AS3 -> JS. Заменить flash sdk, на web sdk или node sdk — и вуаля, у тебя мощный инструмент для созданий серьезных веб-приложений. Если бы они так сделали, глядишь, сейчас не изобретали бы еше одно колесо, а прочно засели бы на серверах и дали бы буст для производительности в браузерах.
Если честно, не совсем понимаю, что вы имеете ввиду под «ростом систического ядра», и что за статическое ядро. Не могли бы вы рассказать про это? И какая в этом проблема?
На счет Object.setPrototypeOf, не совсем понятно, каким образом прототипное наследование «извне» будет лучше классического (или того же прототипного, но с классическим привкусом, как в ES2015) наследования «изнутри».
Динамически собирать иерархию объектов, с возможностью «множественного наследования», когда порядок подключения родителей имеет значение и порою бывает критическим. Думаю, это не лучший способ построения кодовой базы. Пока это небольшой проект — все будет довольно не плохо, а когда в системе будет несколько сотен сущностей — тут уже будет тяжело поддерживать и разбираться.
Возможно, я не прав или не понимаю этой концепции, но на данный момент у меня такая точка зрения.
По-моему, Web Engineer или более общее Software Engineer очень хорошо описывает уровень программиста.
Программисты должны быть инженерами, а не просто разработчиками (кодерами).
Они должны разбираться в куче всего, что связано с написанием и поддержкой кода + понимать что нужно бизнесу. Если программист не понимает ценности чего-то для бизнеса — он простой кодер.
А из дизайнеров и UI/UX специалистов не нужно делать developer'ов — они и так имеют конкретное различие, верстать не должны, но понимание, как устроена верстка у них должно быть, чтобы не рисовать красивости, которые нереально сложно наложить на html.
> Глупость — обучать разработчиков так, что ни фанатично придерживаются принципов. Не понимая зачем и для чего.
А ведь все просто — стоит лишь объяснить зачем нужен этот принцип! А ведь это элементарно! Этот принцип направлен в первую очередь на обратную совместимость.
Если вы пишите решение для конечного пользователя, который мышкой тыкает — вам на самом деле плевать на этот принцип, у вас скорее всего в любой момент времени есть 1 интерфейс и только 1 его имплементация.
В то же время, если вы разрабатываете продукт, который используют другие программисты (фреймворк какой-то), то в этом случае вы обязаны следовать этому принципу. Ну или вашим продуктом никто не будет пользоваться.
Следовать этому принципу не всегда просто. Но выглядеть это должно так — если метод ожидает на вход List, то никакая имплементация List не должна сломать работу этого метода. ArrayList, LinkedList, CopyOnWriteList — абсолютно все равно, ваш метод должен будет выполнится правильно (если не нарушается синхронизация, т.е. этот принцип про это ничего не говорит).
Но есть множество реализаций, которые не выполняют этот принцип, они удобные и классные, но часто порождают баги (ImmutableList, UnmodifiedList, SkippedList).
Выгода от этого принципа: Вы заменяете одну имплементацию на другую и получаете прирост производительности (LinkedList -> ArrayList, или в редких случаях ArrayList -> LinkedList), Заменяете ArrayList имплементацией из throw и получаете выигрыш по потреблению оперативной памяти.
Да, я в курсе про Flowtype, но заметьте — это сторонний плагин для babel, чтобы расширить стандарт ES. Это даже не proposal для ES7.
Вот прям из всех щелей прет типизация, но ее не пускают и даже не хотят рассматривать. А прет на сколько сильно, что даже пишут свои велосипеды (хинты через jsdoc) и расширения для стандарта как плагины для babel.
Мое сообщеним именно про это и было, что развитие похоже на изобретение нового лучшего велосипеда. И потом годами идет разнобой в спецификации и в реализации. По спеке уже давно есть классы, генераторы и т.д., на деле — только babel спасает, нужно все в ES5 транслировать.
Можно конечно сказать «Да кому нужны пользователи IE!!! Пусть ставят FF или Chrome!», но как ни крути, не везде такой ответ будет адекватным. Да ни FF, ни Chrome не поддерживают еще полноценно ES2015, а уже почти 1 год прошел :)
Из того, что встречалось мне, в качестве решения можно использовать билдер, но в приделах одной функции.
Если нужно выполнить 10 изменений, можно из объекта сделать билдер, на нем произвести эти 10 изменений, и наружу отдать новый экземпляр неизменяемого объекта. Тем самым вместо 10 клонирований будет всего 2.
но это отступничество от идеологии, как и global state =)
Сразу оговорюсь, мне больше нравится немного улучшенный Си-шный вариант ООП: структуры с методами, которые имплементят интерфейсы.
Интерфейсы описывают Тип, «структуры с методами» (так и тянет сказать Класс) — имплементация Типа. Желательно без наследования.
Все взаимодействие ведется либо через Тип, либо через Конкретные «структуры с методами» (когда разные имплементации не подразумеваются).
— это я подразумеваю под определением Класса.
Но и наследование иногда полезно иметь, прототипное наследование всем нравится, а оно имеет ту же суть.
> Возможно FP может помочь? Например Redux с глобальным state и Immutable data
> Поскольку весь код написан на pure functions, никаких посторонних эффектов быть не должно.
1. FP, Immutable data
Не столь принципиально, каким путем пойти, FP и OOP справятся в целом одинаково.
Но вот Immutable data… Immutable data для шутера! В этом случае GC будет вызываться очень часто, ваш ноутбук будет перегреваться, FPS будет… FPS иногда будет =)
2. pure function, global state
если все будет на pure function, то кто будет обновлять состояние?))
>> Но! Почему-то миллионы леменгов хотят принести ООП и систему классов
> Да, это целая проблема. Потому что: https://www.youtube.com/watch?v=wfMtDGfHWpA
Система классов не подразумевает под собою наследование. Более того, с наследование — это действительно боль (в большинстве случаев). Классы очень хорошо можно использовать для типизации (классификации), к примеру, чтобы не в комментах писать, какой тип может приниматься методом, а напрямую в декларации метода его вставить. Так же, система классов позволила бы улучшить производительность рантайма: точно известно сколько памяти нужно выделить, точно известно, сколько полей есть в классе, известны ссылки на методы и т.д.
ps: у этого парня в примере есть грубая ошибка: он использует один state для разного поведения, и если несколько поведений будут использовать одну и ту же проперти в state — будет очень больно.
т.е. код красивый, но при первом же конфликте пропертей все будет работать очень странно.
Зачем? Почему не взять html+js и не написать нормальный фронтенд без свистоплясок и трансформаций?
js+html выучить в разы легче, чем ту же java, думаю, js+html выучить легче, чем нормально разобраться в GWT =)
А если так — зачем использовать GWT и вливать в него ресурсы?
В данном случае это java. Самая большая глупость, которую можно сделать — это генерировать js из java кода. Это кажется хорошей идеей, т.к. позволяет изучить только один язык и развиваться в нем.
На самом же деле — это лишь вводит ограничения в проект. Фронтенд уходит далеко вперед, а технологии, которые занимаются извращением — не могут поспевать за этим всем.
> Какие особенности синтаксиса и семантики языка делают его пригодным для написания кода на фронтэнде, а какие — на бэкэнде?
Если мы говорим про web, а мы говорим про него, то ответ очевиден: Главная особенность фронтенд языка — он поддерживается браузерами без плагинов.
> почему GWT — это плохая технология?
Мой позиция в том, что бекенд (в виде java) не должен заниматься рендером html, и уж тем более не должен заниматься кодогенерацией js. Есть конечно случаи, когда без серверного рендера не обойтись, но это можно сделать и без GWT. А вот генерировать js — это и вовсе плохая идея, по-моему, и ни одной адекватной причины для этого не вижу.
У проектов, где бекенд занимается рендером страниц нету возможности расширяться в сторону поддержки мобильных девайсов (или эта поддержка требует много усилий).
Конечно это все субъективно, кому-то нравится, кому-то нет.
Ну и типы действительно делают язык более сложным.
Сложным становится потому, что:
1. движки должны переписаться для поддержки типов, думаю, на данный момент это сложно сделать;
2. разработчикам вместо того, чтобы закинуть просто объект, нужно будет сделать инстанс типа, а это больше кода и в консоли браузера тяжелее тестировать, и вообще «верните мне мой js» (когда-то был крик души на хабре про появление Angular в мире js).
Да, когда-то это было обосновано и, возможно, даже правильно. Но вот уже лет 5-10 как, это все стоит выкинуть на свалку истории, но до сих пор люди мучаются с поддержкой этого легаси.
Да, есть удобство от типизиации, но в целом спроектирован TS криво.
Сейчас они вроде перешли на более-менее нормальный импорт зависимостей (из ES2015), а раньше у них импорт описывался через комментарии с xml тегом.
Да и в целом, они решили сделать велосипед, в то время, когда их нужды без проблем мог покрыть ActionScript3. Который уже на тот момент имел поддержку во многих IDE, который был известен очень большому количеству людей и был очень даже популярным.
Паттерны «стратегия», если простыми словами, — это когда аргумент функции (метода) принимает на вход «кусок кода» (функцию или имплементацию стратегии в ООП), который будет выполняться в работе данной функции.
Т.е. когда есть конкретная функция, которая вызывает другие конкретные функции — это не будет стратегией.
Функция, которая вызывает другую функцию для получения третьей (к примеру керринг) — это не будет стратегией.
А вот если в функцию передается другая функция — это уже будет стратегией.
> асинхронная обработка с continuation-passing
Это больше похоже на стратегию. Вызывается асинхронный метод, и в него передается callback функция, в которую будет выполнен возврат после выполнения асинхронного кода.
Chain of responsibility — это механизм, лежащий в основе pub/sub.
Dispatcher (на него подписываются и через него кидают event) внутри содержит хеш-таблицу сообщение -> список слушателей. Когда кто-то публикует сообщение, диспатчер берет список слушателей и поочередно выполняет их.
(но тут я не прав: «chain of responsibility по учебнику» будет заходить в каждую функцию и говорить «пришло такое сообщение», функция же должна будет посмотреть на это сообщение и решить выполнять над ним логику или нет)
А так же, в сигнатуре метода не стоит объявлять конкретный класс, т.к. дальше этот метод не сможет быть переиспользован с другими стримами. Тут нужен композитный интерфейс, который наследуется от обоих, и все имплементации должны идти от композитного.
На счет Object.setPrototypeOf, не совсем понятно, каким образом прототипное наследование «извне» будет лучше классического (или того же прототипного, но с классическим привкусом, как в ES2015) наследования «изнутри».
Динамически собирать иерархию объектов, с возможностью «множественного наследования», когда порядок подключения родителей имеет значение и порою бывает критическим. Думаю, это не лучший способ построения кодовой базы. Пока это небольшой проект — все будет довольно не плохо, а когда в системе будет несколько сотен сущностей — тут уже будет тяжело поддерживать и разбираться.
Возможно, я не прав или не понимаю этой концепции, но на данный момент у меня такая точка зрения.
Программисты должны быть инженерами, а не просто разработчиками (кодерами).
Они должны разбираться в куче всего, что связано с написанием и поддержкой кода + понимать что нужно бизнесу. Если программист не понимает ценности чего-то для бизнеса — он простой кодер.
А из дизайнеров и UI/UX специалистов не нужно делать developer'ов — они и так имеют конкретное различие, верстать не должны, но понимание, как устроена верстка у них должно быть, чтобы не рисовать красивости, которые нереально сложно наложить на html.
А ведь все просто — стоит лишь объяснить зачем нужен этот принцип! А ведь это элементарно! Этот принцип направлен в первую очередь на обратную совместимость.
Если вы пишите решение для конечного пользователя, который мышкой тыкает — вам на самом деле плевать на этот принцип, у вас скорее всего в любой момент времени есть 1 интерфейс и только 1 его имплементация.
В то же время, если вы разрабатываете продукт, который используют другие программисты (фреймворк какой-то), то в этом случае вы обязаны следовать этому принципу. Ну или вашим продуктом никто не будет пользоваться.
Следовать этому принципу не всегда просто. Но выглядеть это должно так — если метод ожидает на вход List, то никакая имплементация List не должна сломать работу этого метода. ArrayList, LinkedList, CopyOnWriteList — абсолютно все равно, ваш метод должен будет выполнится правильно (если не нарушается синхронизация, т.е. этот принцип про это ничего не говорит).
Но есть множество реализаций, которые не выполняют этот принцип, они удобные и классные, но часто порождают баги (ImmutableList, UnmodifiedList, SkippedList).
Выгода от этого принципа: Вы заменяете одну имплементацию на другую и получаете прирост производительности (LinkedList -> ArrayList, или в редких случаях ArrayList -> LinkedList), Заменяете ArrayList имплементацией из throw и получаете выигрыш по потреблению оперативной памяти.
Вот прям из всех щелей прет типизация, но ее не пускают и даже не хотят рассматривать. А прет на сколько сильно, что даже пишут свои велосипеды (хинты через jsdoc) и расширения для стандарта как плагины для babel.
Мое сообщеним именно про это и было, что развитие похоже на изобретение нового лучшего велосипеда. И потом годами идет разнобой в спецификации и в реализации. По спеке уже давно есть классы, генераторы и т.д., на деле — только babel спасает, нужно все в ES5 транслировать.
Можно конечно сказать «Да кому нужны пользователи IE!!! Пусть ставят FF или Chrome!», но как ни крути, не везде такой ответ будет адекватным. Да ни FF, ни Chrome не поддерживают еще полноценно ES2015, а уже почти 1 год прошел :)
Если нужно выполнить 10 изменений, можно из объекта сделать билдер, на нем произвести эти 10 изменений, и наружу отдать новый экземпляр неизменяемого объекта. Тем самым вместо 10 клонирований будет всего 2.
но это отступничество от идеологии, как и global state =)
Сразу оговорюсь, мне больше нравится немного улучшенный Си-шный вариант ООП: структуры с методами, которые имплементят интерфейсы.
Интерфейсы описывают Тип, «структуры с методами» (так и тянет сказать Класс) — имплементация Типа. Желательно без наследования.
Все взаимодействие ведется либо через Тип, либо через Конкретные «структуры с методами» (когда разные имплементации не подразумеваются).
— это я подразумеваю под определением Класса.
Но и наследование иногда полезно иметь, прототипное наследование всем нравится, а оно имеет ту же суть.
> Поскольку весь код написан на pure functions, никаких посторонних эффектов быть не должно.
1. FP, Immutable data
Не столь принципиально, каким путем пойти, FP и OOP справятся в целом одинаково.
Но вот Immutable data… Immutable data для шутера! В этом случае GC будет вызываться очень часто, ваш ноутбук будет перегреваться, FPS будет… FPS иногда будет =)
2. pure function, global state
если все будет на pure function, то кто будет обновлять состояние?))
> Да, это целая проблема. Потому что: https://www.youtube.com/watch?v=wfMtDGfHWpA
Система классов не подразумевает под собою наследование. Более того, с наследование — это действительно боль (в большинстве случаев). Классы очень хорошо можно использовать для типизации (классификации), к примеру, чтобы не в комментах писать, какой тип может приниматься методом, а напрямую в декларации метода его вставить. Так же, система классов позволила бы улучшить производительность рантайма: точно известно сколько памяти нужно выделить, точно известно, сколько полей есть в классе, известны ссылки на методы и т.д.
ps: у этого парня в примере есть грубая ошибка: он использует один state для разного поведения, и если несколько поведений будут использовать одну и ту же проперти в state — будет очень больно.
т.е. код красивый, но при первом же конфликте пропертей все будет работать очень странно.