В каком фрайворке можно легко манипулировать полями?

6yrop

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

Hastya

Salesforce

6yrop

Salesforce
это что?
гугл первой строкой выдает ссылку на какое-то говнище "without writing any code"
http://www.google.com/search?q=Salesforce&oq=Salesforce...

kokoc88

На какой платформе и в каком фрейворке можно легко добавлять, удалять, менять обработку поля?
Ответ очевиден. C#, по сравнению с которым все языки - говно. ReSharper, без которого C# - тоже говно. И Controllable Query, без которого C#, даже с ReSharper - говно.

6yrop

Ответ очевиден. C#, по сравнению с которым все языки - говно. ReSharper, без которого C# - тоже говно. И Controllable Query, без которого C#, даже с ReSharper - говно.
На C# нет UI движка, который бы не забросили, поэтому твой "очевидный" ответ неправильный.

6yrop

Кстати, вопрос в subject является архитектурным вопросом. :grin:
Предполагаю, многие архитекторы покажут свой уровень, если поговорить с ними на эту тему.

YUAL

Я ищу такой фреймворк для хуй знает какого языка под хуй знает какую-задачу, чтоб работал с хуй знает какой базой данной и позволял "протаскивать и искать сферические поля в вакууме от базы данных до сферического UI".
Под описание проходит qt?

luna89

Берешь nodejs - официально поддерживаемую Microsoft платформу разработки, react.js - state of the art фреймворк для построения пользовательских интерфейсов, и тайпскрипт, про который ты сам говорил, что программировать на нем не менее удобно чем на C#.
Задача решена.

luna89

Еще, конечно, имплементировать Controllable Query для тайпскрипта, чтобы можно было писать npm install cotrollable-query.

jakal222

Еще, конечно, имплементировать Controllable Query для тайпскрипта, чтобы можно было писать npm install cotrollable-query.
а может быть обычный asm? ведь возможности безграничны!

6yrop

айпскрипт, про который ты сам говорил, что программировать на нем не менее удобно чем на C#
я вроде такого не говорил

kokoc88

что программировать на нем не менее удобно чем на C#
Ничего подобного. ReTypscer ещё не вышел в релиз.

Andr163

Вы вот смеетесь, а React + TypeScript вполне себе

zya369

Я ищу такой фреймворк для хуй знает какого языка под хуй знает какую-задачу, чтоб работал с хуй знает какой базой данной и чтобы работал с ним... oh wait

6yrop

Берешь nodejs
На node.js можно писать без event-driven? Или это обязаловка? Проблемы высоких нагрузок и масштабируемости мало для кого действительно актуальны (для нас обычного железа и .NET даже без wait async хватает за глаза, если сами глупости не делать). Код от event-driven, как не крути, усложняется. Если event-driven не нужен, то это неоправданное усложнение кода. В энтерпрайзе хватит и своей сложности (в бизнес логике).

6yrop

Еще, конечно, имплементировать Controllable Query для тайпскрипта, чтобы можно было писать npm install cotrollable-query.
Во-первых, для того, чтобы реализовать Controllable Query для TS надо глубоко погрузиться в компилятор TS. У меня не так много времени.
Во-вторых, в node.js event-driven, который редко нужен.
Поэтому на сервере переходить на js пока не оправдано. Не хочется быть первопроходцем. Пусть другие наработаю шишки на применении node.js в энтерпрайзе.
Сейчас видится приемлемым вариант: на клиенте ts на сервере .NET. Придется написать кодогенератор для remote facade и DTO. Из UI движков сейчас html/js чувствует себя лучше всех. Существенный минус разных языков на клиенте и сервере это то, что нельзя шарить код между сервером и клиентом. Это обычно требуется для валидации и расчетов над данными. Придется для этого лишний раз обращаться на сервер. По моим оценкам это не очень критично для наших приложений.
Меня не покидает ощущение, почему проблему subj-а никто не решает целиком, все что-то пилят на своем отрезке, и преподносят это как инновации.

luna89

На node.js можно писать без event-driven? Или это обязаловка?
В API ноды функции идут парами синхронная/асинхронная (readFileSync/readFile). В принципе, можно писать на ноде CGI или FCGI воркеры чисто на синхронных функциях.

luna89

Сейчас видится приемлемым вариант: на клиенте ts на сервере .NET. Придется написать кодогенератор для remote facade и DTO.
Это, кстати, ошибка - делать клиент-серверное взаимодействие в виде RPC.
Нужен язык описания схемы данных, из которого одновременно выводился бы протокол синхронизации, учитывающий конкарренси, валидацию, батчинг и пуш с сервера, что-то в духе GraphQL/Relay.

6yrop

Это, кстати, ошибка - делать клиент-серверное взаимодействие в виде RPC.
Нужен язык описания схемы данных, из которого одновременно выводился бы протокол синхронизации, учитывающий конкарренси, валидацию, батчинг и пуш с сервера, что-то в духе GraphQL/Relay.
Подозреваю это ошибка, если не хочется писать код. А код не хочется писать из-за того, что нужно быть внимательным и много чего помнить. Эти проблемы присущи динамическому js. На хорошем статически типизированном языке код не только просто писать, но код еще и помогает строить бизнес логику.
 
GraphQL

Что-то это мне сильно напоминает :smirk: ...... ORM на клиенте.
Слава богу, в мире .NET есть здравые люди, которые не скрывают реляционную базу под ORM. Например, вот. Ну и Controllable Query, конечно, освобождает от ORM.
Нас не заманишь в очередную реинкарнацию ORM. Мы любим SQL базы.
Незачем скрывать ни базу, ни сервер.
Использовать такие навороченные абстракции от Файсбука очень рискованно. Они то себе, если надо, что-то смогут поменять во врейворке. У тебя же не такой бюджет проекта. Тянуть тяжелые абстракции в проект надо очень осторожно.

luna89

Подозреваю это ошибка, если не хочется писать код. А код не хочется писать из-за того, что нужно быть внимательным и много чего помнить.
Как ты реализуешь следующую функциональность - в форуме в обычном режиме выводится список тредов с рейтингом первого сообщения, в cyперлайт режиме без рейтинга? Как ты организуешь API?

luna89

Нас не заманишь в очередную реинкарнацию ORM. Мы любим SQL базы.
GraphQL - это просто альтернативный синтаксис запросов, который прозрачно транслируется в SQL. Вот, к примеру, весь транслятор занимает 500 строк, при том что написан на не самом удобном для этого языке.

6yrop

GraphQL - это просто альтернативный синтаксис запросов, который прозрачно транслируется в SQL.
А зачем? В истории было так, сначала были базы на основе графов, потом их вытеснила красивая реляционка (1982 год). Теперь молодняк хочет пройти этим путем еще раз?
Зачем работать с графами? С отношениями же проще работать? Id-шники туда сюда можно гонять, красота же.
И, соответственно, не понятно нахера язык запросов, завязанный на графы? Чем няшный SQL, завязанный на отношения, не угодил?

kill-still

Эээ, неуважаемый, а как же DevExpress? :mad:

6yrop

Эээ, неуважаемый, а как же DevExpress?
У DevExpress куча продуктов, ты, мудила, что имеешь ввиду?

luna89

GraphQL
На праздниках вышло обновление в блоге React.js про GraphQL. Там как раз твоя проблема адресуется:
GraphQL is strongly-typed. Given a query, tooling can ensure that the query is both syntactically correct and valid within the GraphQL type system before execution, i.e. at development time, and the server can make certain guarantees about the shape and nature of the response. This makes it easier to build high quality client tools.
Надо отметить, что фейсбуковский стек GraphQL/Relay/FlowType решает проблему, которую невозожно решить в рамках дотнета и C#. Рассмотрим для примера наш форум с тредами. Где-то нужно получить список тредов, где-то список тредов плюс автора каждого треда, где-то список тредов и с первым постом в каждом треде. Сейчас единственный вариант - делать отдельную функцию в RPC, что, во-первых, приводит к комбинаторному взрыву, а во вторых каждая функция в RPC будет возвращать свой уникальный тип, и типы будут совершенно несовместимы друг с другом. В случае с GraphQL, каждый клиент (например web, iphone или android) для каждой страницы зафетчит именно те данные, которые нужны для этой страницы. Компилятор flowtype будет выводить тип прямо из graphql запроса на получение данных, так как в flowtype нет номинальных типов, все типы структурные.

luna89

Я, кстати, форуме про структурную типизацию в применении к оперденям.

6yrop

проблему, которую невозожно решить в рамках дотнета и C#. Рассмотрим для примера наш форум с тредами.
Не поверишь, но на C# можно написать форум. :)

6yrop

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

Зачем совместимость для разных DTO?

luna89

Опиши более ясно требования к программному обеспечения, которые порождают комбинаторный взрыв? Пока в твоем посте таких требований нет.
Я процитирую сразу инженеров фейсбука (добавлю от себя, что сейчас наблюдаю все описанное у себя на работе):
Invariably fields and additional data are added to REST endpoints as the system requirements change. However, old clients also receive this additional data as well, because the data fetching specification is encoded on the server rather than the client. As result, these payloads tend to grow over time for all clients. When this becomes a problem for a system, one solution is to overlay a versioning system onto the REST endpoints. Versioning also complicates a server, and results in code duplication, spaghetti code, or a sophisticated, hand-rolled infrastructure to manage it. Another solution to limit over-fetching is to provide multiple views – such as “compact” vs “full” – of the same REST endpoint, however this coarse granularity often does not offer adequate flexibility.
...
These systems typically define a custom endpoint per view. For systems with a wide surface area this can quickly grow into a maintenance nightmare of orphaned endpoints, inconsistent tooling, and massive server code duplication. Disciplined engineering organizations can mitigate these issues with great engineering practices, high quality abstractions, and custom tooling. However, given our experience we believe that custom endpoints tend to lead to entropic server codebases.

Зачем совместимость для разных DTO?

Простейший случай - это когда данные показываются в разных контекстах. Например, есть соцсеть, там можно искать пользователей по имени и видеть список, а можно смотреть друзей какого-то пользователя. Есть скажем, запрос GET /users?name=:name, который возвращает [{name, avatar}] и запрос GET /users/:id/friends, который возвращает [{name, avatar, friendSince}]. Мы хотим для обоих случаев использовать общий UI компонент UsersList. Но его невозможно написать, так как типы разные.

6yrop

Я процитирую сразу инженеров фейсбука (добавлю от себя, что сейчас наблюдаю все описанное у себя на работе):
Всё процитированное это описание проблем динамической типизации. И в Файсбуке и у тебя на работе динамика (и на сервере и на клиенте).
Ты же по неизвестной причине переносишь это на типизированный Remote Facade. Почему?
 
Простейший случай - это когда данные показываются в разных контекстах. Например, есть соцсеть, там можно искать пользователей по имени и видеть список, а можно смотреть друзей какого-то пользователя. Есть скажем, запрос GET /users?name=:name, который возвращает [{name, avatar}] и запрос GET /users/:id/friends, который возвращает [{name, avatar, friendSince}]. Мы хотим для обоих случаев использовать общий UI компонент UsersList. Но его невозможно написать, так как типы разные.

Откуда ты заключил, что невозможно?
Начинаешь писать UsersList, ага ему требуется такие-то данные, делаешь под эти требования тип XXX (типа view model, но я бы не заморачивался этой терминологией, исходил бы из реальных потребностей). Из DTO тем или иным способом делаешь XXX. В простейшем случае, просто DTO реализуют интерфейс XXX.

luna89

Всё процитированное это описание проблем динамической типизации.
Динамика помогает срезать острые углы в задачах, с которыми традиционные системы типов не справляются. Представь форум, есть запрос GET /threads. В разных приложениях (айфон, андроид, веб, мобайл веб) для каждого треда могут выводиться (или не выводиться) свойтва author, firstPost, posts, viewsCount итд. Если мы достаем свойства, которые потом не отображаются в UI, то это оверфетчинг и мы зря напрягаем сервер. Иногда добавляют флаги типа GET /threads?withFirstPost=true или /threads?withAuthor=true. 3 опциональных атрибута дают комбинацию из 8 типов. Желаю удачи в написании 8 разных функций в RPC, с уникальным типом у каждой, и в дальнейшем ручном написании интерфейсов на клиенте, которые будут заворачивать каждый из этих типов.

6yrop

всего 4 устройства, поэтому методов должно быть меньше или равно 4
UI на каждом устройстве свой
Проблема надуманная.

zya369

Иногда добавляют флаги типа GET /threads?withFirstPost=true или /threads?withAuthor=true. 3 опциональных атрибута дают комбинацию из 8 типов. Желаю удачи в написании 8 разных функций в RPC
зачем 8 функций-то? для сложения двух чисел ты тоже на каждую комбинацию параметров по функции писать будешь?

6yrop

зачем 8 функций-то? для сложения двух чисел ты тоже на каждую комбинацию параметров по функции писать будешь?
он намекает, что тип возвращаемого значения разный

Dasar

всего 4 устройства, поэтому методов должно быть меньше или равно 4
Неверно.
Hint: В каждом устройстве форм отображения много больше, чем одна.

6yrop

Неверно.
Верно.
Hint: См. контекст разговора.

luna89

 Новая презентация по GraphQL, которая объясняет, как работает обновление данных на сервере с точки зрения клиента GraphQL. Вот мой короткий пересказ.
До появления реакта UI кодировался следующим образом: пользователь совершал действие, мы на каждое действие смотрели, какие виджеты надо изменить по результатам этого действия, и вызывали API этих виджетов. Основная трудность была в том, что для каждого виджета надо было помнить, как изменения в этом виджете могут повлиять на другие виджеты, и неявно реализовывать эти зависимости в коде.
С реактом у нас другая парадигма программирования - у нас есть application state, который представляет собой набор данных, которыми оперирует программа, без дублирования данных и вычисляемых данных, и чистая функция render: application state -> UI. При любом событии мы меняем application state, и у нас вызывается функция render, которая вычисляет все вычисляемые данные и отображает их. Такой подход избавляет нас от необходимости помнить, что при нажатии на кнопку A надо задисейблить кнопку B - при нажатии на кнопку A мы просто обновляем application state, при этом весь интерфейс, включая кнопку B, перерисовывается, а при отрисовке B просто вызывается чистая функция isButtonBDisabled(application state).
Таким образом, убирается связь каждого виджета с каждым, которая квадратично раздувает код. В качестве "единого источника правды" выступает application state, а UI просто вычисляется на основе него чистой функцией.
Но похожая проблема остается, когда речь идет о взаимодействии с сервером. Рассмотрим простейший пример - отображается пост с кнопкой like и списком пользователей, которые лайкнули пост. Когда мы жмем like, то мы добавляемся в список лайков, и количество лайков увеличивается на единицу. Есть два способа реализовать такую функциональность.
Первый способ - после того как отработает запрос на лайк, мы вручную увеличим счетчик на единицу на клиенте, и вставим в массив пользователей, лайкнувших пост, текущего пользователя. Этот способ плох тем, что приходится дублировать логику, уже реализованную на сервере.
Второй способ - дождаться когда отработает запрос на лайк, и заново запросить все данные о посте. Этот способ плох тем, что все равно приходится делать ручную работу - искать старый пост в массиве постов и заменять его на новый пост, пришедший с сервера. Кроме того, происходит удвоение latency - сначала мы должны дождаться, когда отработает запрос на изменение данных, и только после этого заново запрашивать данные.
Фейсбук решает эту проблему следующим образом. Все персистентные данные, зафетченные на клиент, хранятся в некой клиентской базе данных. Эта база данных никогда не редактируется напрямую - сначала всегда делается запрос на сервер. Для каждого типа запроса (например лайк пользователя) прописывается, какие данные инвалидируются этим запросом. Например, запрос like инвалидирует параметры like_count и like_users поста. Это прописывается явным декларативным образом. На основе этих данных, после запроса на like, в респонсе возвращаются новые значения для этих полей, и клиентская база данных обновляется.
Таким образом, принцип unidirectional dataflow распространяется на все приложение целиком - пользователь делает действие в UI, отправляется запрос на сервер, сервер меняет данные и они автоматически обновляются на клиенте, после этого вызывается реакт который перерисовывает UI. Круг замкнулся.
Можно отметить, что в данном подходе все равно, кто инициировал обновление данных - сам пользователь, или кто-то еще - в любом случае, клиентское приложение никогда само не обновляет себе данные, это делает только сервер. Таким образом, получаем простую модель, в которой легко реализуется server push.

kokoc88

До появления реакта UI кодировался следующим образом: пользователь совершал действие, мы на каждое действие смотрели, какие виджеты надо изменить по результатам этого действия, и вызывали API этих виджетов. Основная трудность была в том, что для каждого виджета надо было помнить, как изменения в этом виджете могут повлиять на другие виджеты, и неявно реализовывать эти зависимости в коде.
Твоя проблема в том, что ты раз за разом выдумываешь или находишь ситуации, которые возможны только при написании кода junior разработчиками без code review. Мне очень сложно представить, как C# заставляет меня помнить, какие изменения конкретного представления влияют на что-то, кроме этого самого представления. И тот факт, что какую-то задачу по неопытности можно решить именно так, говорит только о неопытности.
Учти, что собеседования в Facebook нереально олимпиадные. (Я сужу по careercup.) Олимпиадное программирование очень сильно расходится с инженерными буднями. По этой или другим причинам код в Facebook такой, что вызывает во мне первобытный ужас. Так что глупо отталкиваться от того, какие проблемы при разработке интерфейсов были у какой-то конкретной команды. Они сделали какую-то хорошую технологию, но это не значит, что какая-нибудь Java ограничена в качественной реализации схожего функционала.
Таким образом, убирается связь каждого виджета с каждым, которая квадратично раздувает код. В качестве "единого источника правды" выступает application state, а UI просто вычисляется на основе него чистой функцией.

Всё то же самое получается и при наличии stateful представлений. Только UI не вычисляется, а просто отображает своё состояние, которое не является источником правды ни для кого и ни для чего, кроме этого UI. Откуда берётся связь каждого виджета с каждым я вообще понять не смог, хоть и пытался.
Фейсбук решает эту проблему следующим образом. Все персистентные данные, зафетченные на клиент, хранятся в некой клиентской базе данных. Эта база данных никогда не редактируется напрямую - сначала всегда делается запрос на сервер. Для каждого типа запроса (например лайк пользователя) прописывается, какие данные инвалидируются этим запросом. Например, запрос like инвалидирует параметры like_count и like_users поста. Это прописывается явным декларативным образом. На основе этих данных, после запроса на like, в респонсе возвращаются новые значения для этих полей, и клиентская база данных обновляется.
Это всё неплохо реализуется и на C# Под базой данных надо будет понимать иерархию объектов в модели представления. Частичное обновление этой модели можно реализовать как чисто ООП способом, так и через reflection. Правда я опять не понял, зачем явно прописывать, какие поля инвалидируются, когда их можно просто сразу вернуть в ответ на запрос.

6yrop

код в Facebook такой, что вызывает во мне первобытный ужас.
Этот код в публичном доступе? Если да, запости, пожалуйста, ссылку.

kokoc88

Этот код в публичном доступе? Если да, запости, пожалуйста, ссылку.
В Facebook полно кода в публичном доступе. Зачем ждать от меня ссылку, если можно кликнуть первый же результат поиска? Вот, 3-4 клика от топа поиска, и я вижу такой код:
    AndroidBinaryGraphEnhancer graphEnhancer = new AndroidBinaryGraphEnhancer(
params,
resolver,
compressionMode,
resourceFilter,
addFallbackLocales(args.locales.or(ImmutableSet.<String>of
args.manifest,
packageType,
ImmutableSet.copyOf(args.cpuFilters.get
args.buildStringSourceMap.or(false
shouldPreDex,
AndroidBinary.getPrimaryDexPath(params.getBuildTarget
dexSplitMode,
ImmutableSet.copyOf(args.noDx.or(ImmutableSet.<BuildTarget>of
/* resourcesToExclude */ ImmutableSet.<BuildTarget>of
args.skipCrunchPngs.or(false
javacOptions,
exopackageModes,
(Keystore) keystore,
args.buildConfigValues.get
args.buildConfigValuesFile,
nativePlatforms,
dxExecutorService);

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

luna89

Вот, 3-4 клика от топа поиска, и я вижу такой код:
Вроде типичная джава-параша, разве нет? Если сравнивать с каким-нибудь мавеном или антом, то даже лучше выглядит.

kokoc88

Вроде типичная джава-параша, разве нет? Если сравнивать с каким-нибудь мавеном или антом, то даже лучше выглядит.
Найти плохой код можно на любом языке. В данном случае я искал что-то не очень красивое на Java.

luna89

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

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

kokoc88

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

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

luna89

то пример какого-то куска кода. На деле я не вижу, откуда эта связь берётся. Почему в ней есть необходимость.
Как ты решишь эту задачу - 3 поля ввода, синхронизированных друг с другом?
Подозреваю, что ты предложишь что-то на тему publish/subscribe.

kokoc88

Как ты решишь эту задачу - 3 поля ввода, синхронизированных друг с другом?
Подозреваю, что ты предложишь что-то на тему publish/subscribe.
Ты предлагаешь мне написать кода в размере O(E^2 где E - количество полей ввода? Знаешь, даже если решить её с помощью такого кода, то для связи типа "каждый с каждым" кроме этих полей нужно обновлять все остальные компоненты интерфейса. Давай выражаться точнее: некоторые компоненты должны обновлять своё состояние, когда изменяется доменная модель. Ни о каком квадратичном раздувании кода речи на самом деле не идёт.
Ты приводишь пример про кнопки, когда связи между компонентами интерфейса неявные. На практике такой код тяжелее поддерживать и развивать. Например, связать факт неправильного поведения кнопки B при нажатии на кнопку A можно только с помощью анализа data flow. Но этого нельзя сделать с помощью анализа control flow, что на порядки проще. Когда кнопка B вдруг перестала работать как надо, тебе нужно будет осмотреть как логику изменения модели, так и логику отрисовки отдельных компонентов. Да, ты избавился от необходимости помнить, что при нажатии на кнопку A надо обновить кнопку B. Но тебе нужно помнить, что при отрисовке кнопки B нужен корректно работающий метод isEnabled. А ещё тебе нужно помнить, что обработчик кнопки A должен менять какую-то конкретную часть состояния, при чём это знание является неявным, то есть восстанавливать его очень тяжело.
Та же самая ситуация намного легче анализируется и решается, например, с помощью MVP с вариацией Passive View. Presenter обрабатывает сообщение от кнопки A, меняет доменную модель и обновляет кнопку B. Просто поставив в этом месте брейкпоинт можно линейно пройти все шаги и сразу сказать, что происходит. С помощью этого же подхода можно синхронизировать три поля ввода. Если вести речь о разовом решении, то достаточно одного обработчика события, одного поля в доменной модели, и одного метода представления с тремя строчками кода. То есть количество кода получается O(E). Столько же, сколько его в твоём подходе, зато с плюсами наличия явного понимания происходящего.

marat7256

Qt signal/slot не подойдут как варинат?

kokoc88

Qt signal/slot не подойдут как варинат?
Как вариант чего? Получения сообщения от компонента редактирования текста? Подойдут.

luna89

Presenter обрабатывает сообщение от кнопки A, меняет доменную модель и обновляет кнопку B.
Это плохой подход. Если доменная модель меняется в нескольких местах, то тебе в каждом месте надо помнить, что при смене доменной модели надо еще и кнопку B перерисовать. Потом, если у тебя появится кнопка C, то про нее тоже надо помнить. N мест, в которых меняется доменная модель, дают в совокупности c M кнопками сложность M*N. Очевидно, что надо делать decoupling изменятелей доменной модели от отображателей.

luna89

Qt signal/slot не подойдут как варинат?
Это вариация на тему publish/subscribe, к тому же выраженная в рамках убогого языка (вроде требуют какого-то УГ препроцессора).

kokoc88

Это плохой подход. Если доменная модель меняется в нескольких местах, то тебе в каждом месте надо помнить, что при смене доменной модели надо еще и кнопку B перерисовать. Потом, если у тебя появится кнопка C, то про нее тоже надо помнить. N мест, в которых меняется доменная модель, дают в совокупности c M кнопками сложность M*N.
Если доменная модель одинаково меняется в нескольких местах, то это выносится в одну функцию. Откуда ты берёшь M*N мне до сих пор не понятно. Если каждое из N изменений модели обновляет все M мнопок, то сложность одинаковая в обоих примерах, и в худшем случае она будет N + M. Только в моём примере как раз нужно намного меньше держать в голове. Причины описаны выше.
Очевидно, что надо делать decoupling изменятелей доменной модели от отображателей.
Очевидно, что в моём случае это сделано.

6yrop

N мест, в которых меняется доменная модель, дают в совокупности c M кнопками сложность M*N.
Если нет доменной модели, нет этих N мест? Тем самым нет проблемы?

Dasar

ты берёшь M*N
Есть M контролов, которые меняют косвенно N других контролов.

luna89

Если доменная модель одинаково меняется в нескольких местах, то это выносится в одну функцию.
Ок, не мог бы ты показать свой код для примитивного калькулятора, по типу такого?
Насколько я понял, ты сделаешь какие-то функции onChangeFirst и onChangeSecond, в которых будешь вызывать функцию updateSum. Но тогда у тебя информация о связи между слагаемыми и суммой будет закодирована в двух местах: первый раз, явно, в функции вычисления суммы, и еще пару раз, менее явно, в функциях onChangeFirst и onChangeSecond.
PS: я специально написал код с большим количеством бойлерплейта, в реакте то же самое можно сделать меньшим количеством кода.

6yrop

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

6yrop

калькулятор со странностью, не работает привычное поведение, жму BackSpace, ожидаю, что поле ввода очиститься, вместо этого вылазит не стираемый NaN.
Ты отлично продемонстрировал, чем плохи тяжеловесные, слишком умные фрейворки, неожиданно отваливается привычное поведение. И потом программист вынужден разбираться, думать как выкрутиться в парадигме фрейворка. Т.е. accidental complexity, которой нет в исходной задаче.

luna89

калькулятор со странностью, не работает привычное поведение, жму BackSpace, ожидаю, что поле ввода очиститься, вместо этого вылазит не стираемый NaN.
Это аргумент Буратино:
— Предположим, что у вас в кармане два яблока. Некто взял у вас одно яблоко. Сколько у вас осталось яблок?
— Два.
— Подумайте хорошенько.
Буратино сморщился, — так здорово подумал.
— Два…
— Почему?
— Я же не отдам некту яблоко, хоть он дерись!

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

6yrop

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

kokoc88

Есть M контролов, которые меняют косвенно N других контролов.
Есть M контролов, каждый из которых меняет N контролов, при чём каждый раз разные свойства этих N контролов? Даже здесь мне очень и очень сложно придумать решение за N*M. В конечном итоге, если у нас требования на такой долбанутый GUI, то можно написать одну функцию, полностью обновляющую представление.
Замечу, что в случае использования решения, которое пропагандирует DDD, в этом примере тоже мало хорошего. Каждый обработчик из M будет менять N свойств в доменной модели. Все эти свойства надо будет вычитывать во время рендеринга.

kokoc88

Насколько я понял, ты сделаешь какие-то функции onChangeFirst и onChangeSecond, в которых будешь вызывать функцию updateSum. Но тогда у тебя информация о связи между слагаемыми и суммой будет закодирована в двух местах: первый раз, явно, в функции вычисления суммы, и еще пару раз, менее явно, в функциях onChangeFirst и onChangeSecond.
Я напишу псевдо код для твоего примера, чтобы было понятно. Вариантов масса, но я упоминал MVP Passive View, поэтому останусь в этих рамках. Код пишу в лоб, чтобы было максмимально понятно.
class Model
{
int first;

int second;

int Sum { get { return first + second; }
}

class Presenter
{
void OnChanged1(string val)
{
model.first = int.Parse(val);
view.SetSum(sum);
}

void OnChanged2(string val)
{
model.second = int.Parse(val);
view.SetSum(sum);
}
}

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

6yrop

напишу псевдо код
Нечестно. привел полный код. Приведи полный код на твоей любимой платформе.

PooH

Это вариация на тему publish/subscribe, к тому же выраженная в рамках убогого языка (вроде требуют какого-то УГ препроцессора).
попрошу не обижать сигналы-слоты в qt
они там очень даже хорошо сделаны

Dasar

Замечу, что в случае использования решения, которое пропагандирует DDD, в этом примере тоже мало хорошего. Каждый обработчик из M будет менять N свойств в доменной модели. Все эти свойства надо будет вычитывать во время рендеринга.
Это дешево - на сервере вычитать еще раз все данные для всей страницы при каждом самом маленьком изменении. Важно, чтобы при этом страница у пользователя "не мигала".

kokoc88

Нечестно. привел полный код. Приведи полный код на твоей любимой платформе.
Ага, щас запощу с .designer.cs

6yrop

не надо думать
а в каком подходе и над чем надо думать?

kokoc88

Это дешево - на сервере вычитать еще раз все данные для всей страницы при каждом самом маленьком изменении.
Это стоит столько же, сколько и при обычном stateful подходе с полным обновлением view.
- программисту не надо думать, какие контролы надо перерисовать после нажатия кнопки, а какие - не надо.
Какие контролы надо перерисовывать - думать не надо. Зато ему надо думать, как писать рендеринг на основе большого количества свойств. То есть то же самое, что и в классическом подходе плюс куча неявных связей.

Dasar

а в каком подходе и над чем надо думать?
При классическом подходе (например, как у в примере) необходимо думать, что при изменении слагаемого необходимо дернуть перерисовку суммы. В примере для этого появился здоровый класс Presenter.

6yrop

Ага, щас запощу с .designer.cs
Да, так будет честное сравнение.

Dasar

Это стоит столько же, [..]
[..]
То есть то же самое, что и в классическом подходе плюс куча неявных связей.
Вот и выходит, что при подходе от Facebook-а получается кода в 1.5 раза меньше, а накладные расходы при исполнении те же самые.

kokoc88

При классическом подходе (например, как у в примере) необходимо думать, что при изменении слагаемого необходимо дернуть перерисовку суммы. В примере для этого появился здоровый класс Presenter.
А в другом подходе ты руками вычитал свойства модели, о чём тебе тоже надо было подумать. Например, по свойству isCritical нарисовать красную рамку, а по свойству text - содержимое контрола. Мало того, кроме неявных связей между компонентами, мы ещё и завязываем view на доменную модель.

kokoc88

Вот и выходит, что при подходе от Facebook-а получается кода в 1.5 раза меньше, а накладные расходы при исполнении те же самые.
Нет, не выходит. Кода столько же. Расходы те же самые. Плюс сложности в поддержке. Я уже имел дело с таким набором компонентов - старый GUI в Unity очень близок к тому, что здесь описывается. Кроме неявных связей и коуплинга с моделью могу ещё добавить появление свойств, которые относятся только ко view. Например, текущий сфокусированный контрол, выбранный таб, и так далее. Эти свойства появляются в самых неожиданных местах и часто загромождают доменную модель.

kokoc88

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

Dasar

А в другом подходе ты руками вычитал свойства модели, о чём тебе тоже надо было подумать. Например, по свойству isCritical нарисовать красную рамку, а по свойству text - содержимое контрола. Мало того, кроме неявных связей между компонентами, мы ещё и завязываем view на доменную модель.
1. Зачем руками? Вычитываться они будут тем же способом, что и при классике
2. Подход Facebook-а декомпозируется произвольным способом. Соедини View и Model через отдельный класс. Кто мешает?

6yrop

При классическом подходе (например, как у в примере) необходимо думать, что при изменении слагаемого необходимо дернуть перерисовку суммы. В примере для этого появился здоровый класс Presenter.
Самый прямолинейный вариант чем плох?
 

public Form1
{
arg1TextBox.TextChanged += delegate {
UpdateSum;
};
arg2TextBox.TextChanged += delegate {
UpdateSum;
};
UpdateSum;
}

void UpdateSum
{
string sum;
int arg1;
if (int.TryParse(arg1TextBox.Text, out arg1
{
int arg2;
if (int.TryParse(arg2TextBox.Text, out arg2
sum = (arg1 + arg2).ToString;
else
sum = нецелочисленныйАргумент;
}
else
sum = нецелочисленныйАргумент;
sumLabel.Text = sum;
}

const string нецелочисленныйАргумент = "нецелочисленный аргумент";

Dasar

Кроме неявных связей и коуплинга с моделью могу ещё добавить появление свойств, которые относятся только ко view.
Они будут храниться во ViewModel, а не в доменной модели.
Классический подход(указано какой код пишется для каждой формы без учета декомпозиции):

Screen <-- Model
Screen <-- ViewModel
Screen --> Model, UpdateAndRedrawControls(.., Ci, ..)
Screen --> ViewModel, UpdateAndRedrawControls(.., Cj, ..)

Facebook подход

Screen <-- Model
Screen <-- ViewModel
Screen --> Model
Screen --> ViewModel

kokoc88

1. Зачем руками? Вычитываться они будут тем же способом, что и при классике
То есть в обычном подходе кода столько же, если не меньше. В моём случае это будет setText и setBorderColor; в другом случае это будет input value: appState.first border-color: getColor
2. Подход Facebook-а декомпозируется произвольным способом. Соедини View и Model через отдельный класс. Кто мешает?
OK, то есть утверждение "появился громоздкий класс Presenter" снимается. А то ты написал, как будто этот класс появляется в этом паттерне только из-за того, что надо помнить про обновление представлений.

Dasar

Самый прямолинейный вариант, чем плох?
Пример слишком маленький(мало функционала чтобы заметить отличие.
Добавь, пожалуйста, для второго слагаемого замену текстового input-а на combobox(тег select) при нажатом checkbox-е и замену на input обратно при отжатом.
В combo-е значения 0, 1, 5, 10. Уже введенное значение копируется из input-а в combo и обратно. Если в combo-е такого значения нет, то оно временно добавляется.

kokoc88

Facebook подход
Facebook подход другой:

Screen <-- Model, readPropertiesToRender
Screen <-- ViewModel, readPropertiesToRender
Screen --> Model, render
Screen --> ViewModel, render

6yrop

Уже спать пора, код напишу завтра. Всё же любопытно, что там такого будет?

Dasar

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

Screen <-- (sql-запрос) <-- DB
Screen <-- ViewModel
Screen --> DB, UpdateAndRedrawControls(.., Ci, ..)
Screen --> ViewModel, UpdateAndRedrawControls(.., Cj, ..)

Facebook подход

Screen <-- (graphql-запрос) <-- DB
Screen <-- ViewModel
Screen --> DB
Screen --> ViewModel

6yrop

Мало кода, чтобы заметить отличие.
Т.е. по принципу KISS мой вариант лучший?

Dasar

Т.е. по принципу KISS мой вариант лучший?
Переформулирую свою предыдущую ремарку. В примере слишком мало функционала, чтобы заметить отличие.

kokoc88

readPropertiesToRender
Я вот о чём сейчас подумал. В классическом понимании у контролов десятки свойств: цвет рамки, цвет фона, цвет текста, фокус, шрифт текста, и так далее. Чтобы с этим справиться внутри render придётся написать функции рендеринга для всех стандартных контролов. А чтобы с этими функциями было удобно работать, придётся написать стандартные классы, которые будут хранить состояния этих контролов.
В самом деле, если у невалидных полей в какой-нибудь анкете надо рисовать рамку красным цветом, не будем же мы каждому контролу в render приписывать свой border-color: isControlXXXValidColor; а если нам потом придётся всем этим контролам ещё и поменять цвет текста, приписывать каждому text-color: isControlXXXValidTextColor

6yrop

Переформулирую свою предыдущую ремарку. В примере слишком мало функционала, чтобы заметить отличие.
Т.е. вы херово сформулировали проблему. Так сформулируйте нормально, в чем ваша проблема? А то глядишь и проблемы нет. Боритесь с ветряными мельницами.
Вот эпичное описание в 5 частях борьбы с ветряными мельницами:
http://habrahabr.ru/post/50830/
Я начал зевать, где-то на 3-ей части. Скукота.

Dasar

В классическом понимании у контролов десятки свойств: цвет рамки, цвет фона, цвет текста, фокус, шрифт текста, и так далее.
Это же всего два свойства - style и class. Зачем для них обертки делать?
если у невалидных полей в какой-нибудь анкете надо рисовать рамку красным цветом, не будем же мы каждому контролу в render приписывать свой border-color: isControlXXXValidColor;
конечно не будем. Будет написано control.class += data.isInvalid? 'invalid' : null; один раз в обобщенном варианте View для поля.

kokoc88

Это же всего два свойства - style и class. Зачем для них обертки делать?

То есть для двух свойств, которые могут принимать по два значения, мы напишем 4 класса? Так, говоря об N^2... Или два, а потом будем объединять их через обёртки по отдельности?
конечно не будем. Будет написано control.class += data.isInvalid? 'invalid' : null; один раз в обобщенном варианте View для поля.
То есть из stateless контролов мы сделали обычные классические stateful.

Dasar

То есть из stateless контролов мы сделали обычные классические stateful.
Это stateless код.

kokoc88

Это stateless код.
Что такое data, и как там вычисляется isValid?

Dasar

Что такое data, и как там вычисляется isValid?
data - это ViewModel.
Два варианта:
1. Клиентская проверка. IsValid проверяет на какой-нибудь regexp.
2. Серверная проверка. IsValid делает запрос на сервер через graphql и проверяет что значение не конфликтует с БД.

kokoc88

1. Клиентская проверка. IsValid проверяет на какой-нибудь regexp.
Если возраст от 18 до 85 лет, то ты будешь проверять это через regexp? А если для проверки нужны другие поля доменной модели?
Фактически, ты просто полностью вынес состояние контрола в другой stateful класс. И этот класс ты будешь обновлять точно так же, как ты обновлял бы содержимое классического контрола.

Dasar

Фактически, ты просто полностью вынес состояние контрола в другой stateful класс. И этот класс ты будешь обновлять точно так же, как ты обновлял бы содержимое классического контрола.
Не будет никакого statefull-а. Будет stateless (упрощенный вариант, оставляя только суть для понимания).
Кроме этой функции больше никакого кода для rendering-а или изменения отображения писаться не будет.

DomObject View(ViewModel viewModel)
{
var model = graph-ql(..);

var input = new Input;
input.class = 'age';
if (isInt(viewModel.age
{
var age = ToInt(viewModel.age);
if (model.IsPorno && age < 21)
{
input.class += 'invalid';
input.title = 'Таких маленьких на порно не пускают!";
}
}
else
{
input.class += 'invalid';
}
return input;
}

kokoc88

Не будет никакого statefull-а. Будет stateless (упрощенный вариант, оставляя только суть для понимания).
Вариант настолько упрощённый, что пришёл к тому, что я говорил: для каждого текстового поля ты напишешь большой кусок специфичного кода: renderAge renderGender renderPassportNumber и так далее. Чтобы уйти от этого, нам нужно написать InputViewModel, которая фактически инкапсулирует состояние контрола.
Кроме этого в твоём примере остался коуплинг с доменной моделью.

Dasar

Блин! Это же пример!
Ты напираешь на то, что в преобразовании `Screen <-- DB` будет дублирующийся код. Хотя эта проблема, вообще, ортогональна тому, что предлагает facebook.

kokoc88

Ты напираешь на то, что в преобразовании `Screen <-- DB` будет дублирующийся код. Хотя эта проблема, вообще, ортогональна тому, что предлагает facebook.
Я напираю на то, что в реальном проекте мы закончим написанием stateful моделей для всех контролов.

Dasar

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

kokoc88

Не будет этого. Отвлекись от проблемы дублирования кода и покажи в примере выше где там появляется stateful.
Проблема выше является примером, на котором я ничего показать не смогу. Ничего не бывает бесплатно, и в данном случае меня интересует, как ты предлагаешь рендерить Input. Либо ты пишешь огромное количество кода и огребаешь, когда нужно что-то доработать. Либо ты делаешь универсальную функицю, которая рендерит универсальную stateful модель. Я пока что не увидел у тебя никакого третьего варианта.

luna89

Я напираю на то, что в реальном проекте мы закончим написанием stateful моделей для всех контролов.
Можешь сформулировать задачу? Я могу показать код (хочется поевангелировать за реакт в частности и за js вообще).

kokoc88

Можешь сформулировать задачу? Я могу показать код (хочется поевангелировать за реакт в частности и за js вообще).
Задачи у меня в голове прямо сейчас нет. Я рассматриваю твой подход с точки зрения реализации большого многолетнего проекта. Очевидно, что рендерить каждый Input со всеми его свойствами, вычитывая их прямо в коде рендеринга конкретного GUI, в таком проекте глупо. Поэтому нам потребуется универсальная функция, которая сможет отрендерить все возможные Input. Сейчас мне интересно, как будет устроена эта функция.

Dasar

Я пока что не увидел у тебя никакого третьего варианта.
Третий вариант: делается универсальная функция, которая рендерит stateless-модель.

kokoc88

Третий вариант: делается универсальная функция, которая рендерит stateless-модель.
Отлично. То есть каждый раз, когда эта модель меняет свойство isValid, мы создаём её заново и записываем в состояние какой-то другой stateful модели. Фактически, вместо вызова domain.firstInput.isValid = true, я должен буду сделать domain.firstInput = new InputModel(false, domain.isRequired, domain.firstValue)

luna89

Очевидно, что рендерить каждый Input со всеми его свойствами, вычитывая их прямо в коде рендеринга конкретного GUI, в таком проекте глупо.
В большом проекте надо иметь UI гайдлайн и под него наделать стандартных компонентов. Можно сделать компонент MyInput со свойством valid. Этот компонент будет рендерить обычный React.DOM.input, но при valid=false будет ставить красную рамку и красный шрифт.
Самое главное, что у нас везде обычный джаваскрипт (или тайпскрипт код можно рефакторить и декомпозировать как хочешь. Нет выдуманного говна типа каких-то xml шаблонов и птичьих языков, которые надо писать внутри xml.

kokoc88

В большом проекте надо иметь UI гайдлайн и под него наделать стандартных компонентов. Можно сделать компонент MyInput со свойством valid. Этот компонент будет рендерить обычный React.DOM.input, но при valid=false будет ставить красную рамку и красный шрифт.
Как только ты начинаешь добавлять свойства в компонент, он перестаёт идеологически отличаться от классических stateful компонентов. Об этом и речь.
Самое главное, что у нас везде обычный джаваскрипт (или тайпскрипт код можно рефакторить и декомпозировать как хочешь.
Да, сам рендеринг сделан очень хорошо. Как раз для программистов.

Dasar

То есть каждый раз, когда эта модель меняет свойство isValid, мы создаём её заново и записываем в состояние какой-то другой stateful модели
Нет. На каждое изменение генеришь через stateless новый вариант страницы, а react.js прозрачно для пользователя заменяет старый вариант страницы на новый (делая diff и заменяя только те места, которые поменялись)

kokoc88

На каждое изменение генеришь через stateless новый вариант страницы
Ты сам-то понял, что написал?

Dasar

Ты сам-то понял, что написал?
Да. Там написано ровно то, что делает код.

kokoc88

Да. Там написано ровно то, что делает код.
И так, я ввёл 1 в поле возраста. Я правильно понимаю, что в onValueChanged ты пишешь domainModel = new DomainModel?

Dasar

И так, я ввёл 1 в поле возраста. Я правильно понимаю, что в onValueChanged ты пишешь domainModel = new DomainModel?
В примерах выше не было ничего с названием domainModel.

kokoc88

В примерах выше не было ничего с названием domainModel.
В примере выше у тебя доступ к доменной модели был просто через поле model. Но у тебя не было примера, который бы дал ответ на мой главный вопрос: как сделать универсальную функцию рендеринга всех Input.

Dasar

как сделать универсальную функцию рендеринга всех Input.
Также как и неуниверсальную.


DomObject UniversalView(ViewModel viewModel, GraphQlModel model, string field)
{
var input = new Input;
input.class = field;

if (isInt(viewModel[field]
{
input.class += 'invalid';
}
return input;
}

DomObject View(ViewModel viewModel)
{

var nonUniversalHandlers = new[]{new {field = 'age', graph-ql = graph-ql('is-porno' code = (viewModel, model, input)=>
{
if (model.IsPorno && viewModel['age'] < 21)
{
input.class += 'invalid';
input.title = 'Таких маленьких на порно не пускают!";
}
}
var fields = new[]{'age', 'weight', 'height', 'width'};

var model = graph-ql(graph-ql-merge(fields, nonUniversal.Select(_=>_.graph-ql;

var panel = new Panel;
foreach (var field in fields)
{
var input = UniversalView(viewModel, model, field);
var handler = nonUniversalHandlers.FirstOrDefault(_=>_.field == field);
if (handler != null)
handler.code(model, input);
panel.Add(input);
}

return panel;
}

kokoc88

isInt(viewModel[field])
viewModel[field] - это состояние, которое может изменяться. Где и как ты собираешься это делать?

Dasar

viewModel[field] - это состояние, которое может изменяться.
Это временное состояние с длиной жизни в цикл перерисовки.
void redraw
{
var viewmodel = CollectViewModel(window);
var view = View(viewmodel);
react.redraw(window, view);
}

kokoc88

Это временное состояние с длиной жизни в цикл перерисовки.
То есть на каждую нажатую клавишу, на каждое движение скролла я создаю целую иерархию классов только для отображения представления? Прекрасный способ получить тормозной сайт. Ровно то, о чём я писал выше: "Фактически, вместо вызова domain.firstInput.isValid = true, я должен буду сделать domain.firstInput = new InputModel(false, domain.isRequired, domain.firstValue)"
И потом, с какой стати временное состояние перестало быть состоянием?

Dasar

То есть на каждую нажатую клавишу, на каждое движение скролла я создаю целую иерархию классов только для отображения представления?
Что это у тебя за страница такая? На которой на столько много элементов ввода, что они: во-первых требуют иерархию классов, а во вторых - тормозят (получение их значений из контролов в объект - не укладываются в 10мс)?
Элементов ввода на странице обычно десяток. Максимум сотня.
И потом, с какой стати временное состояние перестало быть состоянием?
Состояние имеет свойство изменчивости во времени. Временное состояние не обладает такой изменчивостью.

6yrop

Можешь сформулировать задачу? Я могу показать код (хочется поевангелировать за реакт в частности и за js вообще).
Опа, так у тебя самого нет хорошего примера, который показывает, почему надо использовать react.js, а не работать с DOM напрямую?
У хорошего евангелиста всегда есть такой пример.

6yrop

view.SetSum(sum);
а что такое sum? Это опечатка? Следует читать как model.Sum?

kokoc88

Что это у тебя за страница такая? На которой на столько много элементов ввода, что они: во-первых требуют иерархию классов, а во вторых - тормозят (получение их значений из контролов в объект - не укладываются в 10мс)?
Задержки от 10мс заметны при наборе текста на клавиатуре. Я знаю это, потому что в своё время делал сложный контрол для ввода текста. Но я согласен, давай не будем обсуждать производительность.
Элементов ввода на странице обычно десяток. Максимум сотня.
Элементов ввода - да, но там есть и другие компоненты. Мало того, эти компоненты могут добавляться динамически. А ещё это может быть spreadsheet типа Google Docs.
Состояние имеет свойство изменчивости во времени. Временное состояние не обладает такой изменчивостью.
Во-первых, у тебя обычная доменная модель с состоянием. Тот факт, что ты используешь parameter object для передачи данных в какой-то фреймворк не делает твою систему stateless.
Во-вторых, ещё недавно ты предлагал:
Кроме неявных связей и коуплинга с моделью могу ещё добавить появление свойств, которые относятся только ко view.
Они будут храниться во ViewModel, а не в доменной модели.
И каким же образом эти mutable данные будут храниться в модели, которая создаётся 1 раз для рендеринга?

Dasar

А ещё это может быть spreadsheet типа Google Docs.
Компоненты с большим кол-ва ввода (редакторы текста, spreadsheet-ы) требуют отдельного подхода по множеству причин.
И каким же образом эти mutable данные будут храниться в модели, которая создаётся 1 раз для рендеринга?
Mutable данные хранятся в data-аттрибутах контролов и собираются CollectViewModel.
Это делается для того, чтобы удаление контрола было атомарным и автоматически удалялось всё состояние, которое с ним связано.

kokoc88

Mutable данные хранятся в data-аттрибутах контролов и собираются CollectViewModel.
То есть в итоге мы получаем обычные контролы с состоянием.

Dasar

То есть в итоге мы получаем обычные контролы с состоянием.
Не знаю как ответить на этот вопрос. Предложенное решение от facebook-а одновременно похоже на контролы с состоянием и не похоже: часть свойств решения совпадают, а часть - даёт новые выгоды.
ps
Предложи свой код для решения задачи
Добавь, пожалуйста, для второго слагаемого замену текстового input-а на combobox(тег select) при нажатом checkbox-е и замену на input обратно при отжатом.
В combo-е значения 0, 1, 5, 10. Уже введенное значение копируется из input-а в combo и обратно. Если в combo-е такого значения нет, то оно временно добавляется.
На примере будет проще показать в чём решения совпадают и какие проблемы решаются по новому.

6yrop

Если в combo-е такого значения нет, то оно временно добавляется.
Уточни, что значит временно, значение добавляется, а потом исчезает? Когда исчезает?

kokoc88

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

Зачем это теперь надо, если и ты, и DDD уже подтвердили наличие состояния у контролов. (Простые примеры вы всё равно будете писать без состояния, так что смысла нет.)
Можно сделать компонент MyInput со свойством valid. Этот компонент будет рендерить обычный React.DOM.input, но при valid=false будет ставить красную рамку и красный шрифт.

6yrop


model.first = int.Parse(val);
view.SetSum(model.Sum);
Именно про такие строчки Хельсберг пишет, что это ООП, которое создает проблемы:

Does the object-oriented paradigm create problems?
Anders: You know, it depends on what you group under the object-oriented paradigm. Polymorphism and encapsulation and inheritance are as such not a problem, although functional languages typically have a different view of how you do polymorphism with their algebraic data types. Aside from that, I think the biggest problem typically with object-oriented programming is that people do their object-oriented programming in a very imperative manner where objects encapsulate mutable state and you call methods or send messages to objects that cause them to modify themselves unbeknownst to other people that are referencing these objects. Now you end up with side effects that surprise you that you can't analyze.
In that sense object-oriented programming is a problem, but you could do object-oriented programming with immutable objects. Then you wouldn't have these same problems. That's kind of what functional programming languages are doing, for example.
http://broadcast.oreilly.com/2009/04/an-interview-with-ander...

Dasar

Зачем это теперь надо, если и ты, и DDD уже подтвердили наличие состояния у контролов.
Чуда не бывает, как и контролов без состояния.
Соответственно, подход facebook-а не о том, как избавиться от состояния, а как организовать код так, чтобы раз и навсегда избавиться от проблем, которые несёт за собой наличие состояния.

Dasar

Можно сделать компонент MyInput
Такое решение не масштабируется.

kokoc88

Соответственно, подход facebook-а не о том, как избавиться от состояния, а как организовать код так, чтобы раз и навсегда избавиться от проблем, которые несёт за собой наличие состояния.
Пока что мы договорились до того, что управление контролом размазано по двум местам. Для цвета рамки мы меняем свойство модели контрола, а для отображения данных пишем код в рендиринге. Если всё-таки инкапсулировать, то у нас появится полноценная модель контрола. От каких проблем мы избавляемся я так и не понял.
Такое решение не масштабируется.
Это решение от DDD. Но я не понял, что значит "не масштабируется".

kokoc88

Именно про такие строчки Хельсберг пишет, что это ООП, которое создает проблемы:
Ты собираешься привести пример immutable программы для ввода двух чисел и отображения их суммы?

Dasar

Пока что мы договорились до того, что управление контролом размазано по двум местам.
Не подтверждаю такой договоренности. Управление (в facebook подходе) всегда в одном месте.
Для цвета рамки мы меняем свойство модели контрола, а для отображения данных пишем код в рендиринге.
и то, и другое делается в одном месте: рендеринге.

kokoc88

Не подтверждаю такой договоренности. Управление (в facebook подходе) всегда в одном месте.
Только что ты говорил про наличие mutable view model для хранения состояния, которое нужно только для представления. Ты не согласен с тем, что это состояние изменяется в одном месте, а свойства для отображения используются в коде рендеринга? В твоём же примере будет как parameter object для рендиринга, так и модель с состоянием для контрола. Соответственно, данные parameter object ты получаешь из доменной модели, а mutable свойства меняешь в другом месте.

apl13

вроде требуют какого-то УГ препроцессора
Уже не обязательно.

Dasar

model - это состояние, которое находится на стороне сервера.
view-состояние - это состояние, который не входит в model и которое необходимо для отображения UI. view-состояние включает в себя только такое состояние, которое не вычисляется на основе другого состояния.
Частные случаи view-состояния:
- позиция скроллбара
- позиция курсора
- выбранный элемент
- focus
- введенный текст
Model - это stateless реализация паттерна Репозиторий для доступа к model.
ViewModel - это stateless реализация паттерна Репозиторий для доступа к view-состоянию.
Само view-состояние хранится:
 - или в контролах непосредственно (позиция скроллбара)
 - или в data-аттрибутах (выбранный элемент (при реализации без checkbox-ов
Доступ к состоянию всегда делается через ViewModel, как на получение, так и на изменение.
Отображение (как первоначальное, так и перерисовка при изменении ViewModel и Model) делаются через связку React+Stateless Presenter.
Задача Stateless Presenter предоставить такой вид, который должен быть при текущем состоянии ViewModel и Model.
Задача React-а аккуратно изменить вид в браузере на предоставленный Presenter-ом.
Изменение ViewModel делается из множества мест: по кол-ву управляющих элементов.
Логика отображения на основе текущего состояния ViewModel+Model записана в одном месте: в Presenter-е.

Dasar

react - это паттерн Bridge.
Он заменяет классический сильно-связанный подход - Presenter(предыдущее отображение, ViewModel, Model) на слабо связанный: ReDraw(предыдущее отображение, новое отображение:Presenter(ViewMode, Model

Dasar

Важное дополнение:
vew-состояние включает в себя только такое состояние, которое не вычисляется на основе другого состояния.
Соответственно, цвет рамки и т.д. не входит во view-состояние.

kokoc88

Доступ к состоянию всегда делается через ViewModel, как на получение, так и на изменение.
То есть ViewModel - это обычная модель с mutable состоянием. Никакой разницы с классическим подходом.
Остальную воду, которую ты налил, я пропускаю, потому что ты уехал в какие-то дебри, да ещё и запутываешь в определениях.

kokoc88

Соответственно, цвет рамки и т.д. не входит во view-состояние.
Тогда снова встаёт вопрос, как ты напишешь универсальную функцию для отображения любого поля для ввода текста. У тебя цвет определяется разными isValidXXX для разных полей.

Dasar

> Никакой разницы с классическим подходом. Остальную воду, которую ты налил, я пропускаю
Своё текущее эмоциональное состояние могу передать только анекдотом
Привозит папа сына-дебила на море, привел его на пляж и объясняет:
П: Вот видишь, сынок, это море..
С: (тупо) Где?
П: Ну вот это плещется-это море...
С: (по прежнему тупо)Где?
П:(распаляясь)Ну вот мы сидим на таком желтом-это песок, а вон то синее-это море..
С:...Где?
П:(в бешенстве)Что ты идиот не понимаешь, вот это сухое, желтое-это песок, а вон то синее, плещется, мокрое-ЭТО МОРЕ!
С:(тупо)Где?
Папины нервы не выдерживают, он в крайней ярости хватает сына, и макает его головой в воду с криком "Вот море! Вот оно! Вот это море! Вот!" Сын захлебывается, кое-как вырывается и с трудом дыша спрашивает
-Папа, что это было?
-Море, %*"!
-ГДЕ?

kokoc88

Своё текущее эмоциональное состояние могу передать только анекдотом
Этим анекдотом ты передал моё эмоциональное состояние. Столько громких слов об immutable контролах, но стоило только немного копнуть в сторону реального проекта, как оказалось, что нам нужен универсальный способ рендеринга контролов. Вы предложиди два решения. Либо для каждого компонента сделать модель с mutable state, которая должна использоваться для рендеринга. Либо размазать state этой же модели на mutable и immutable части, которые находятся в разных классах; и работать с этими частями в разных местах кода.
Через декомпозицию Stateless Presenter-а на stateless SubPresenter-ы: обобщенные и частные.
То есть для проверки имени у меня будет класс IsValidNamePresenter, а для проверки возраста IsValidAgePresenter?

Dasar

Тогда снова встаёт вопрос, как ты напишешь универсальную функцию для отображения любого поля для ввода текста. У тебя цвет определяется разными isValidXXX для разных полей.
Эта задача декомпозируется на stateless ValidPresenter и stateless Validator.
ValidPresenter отвечает за отображение валидности.
Validator отвечает за формирование статуса валидности данных.
ValidPresenter-а декомпозируется на независимые stateless ValidSubPresenter-ы. Каждый отвечает за свою часть отображения невалидности: один отображает рамочку, другой выводит ремарку, третий - подчеркивает текст.
Validator декомпозируется на stateless SubValidator-ы.
Задача Validator-а смержить состояния невалидности получаемое от SubValidator-ов

luna89

Столько громких слов об immutable контролах,
Термин mutable/immutable в этом обсуждении ты первый употребил. Я вообще ничего подобного не упоминал.

6yrop

Ты собираешься привести пример immutable программы для ввода двух чисел и отображения их суммы?
вот в этих строчках
 
model.first = int.Parse(val);
view.SetSum(model.Sum);

у тебя mutable поле model.first. Без этого mutable поля можно обойтись. См. мой код выше.

kokoc88

у тебя mutable поле model.first. Без этого mutable поля можно обойтись. См. мой код выше.
Твой код выше - это простой пример. Если ты точно так же пишешь код в больших коммерческих проектах, то у меня для тебя плохие новости.

Dasar

То есть для проверки имени у меня будет класс IsValidNamePresenter, а для проверки возраста IsValidAgePresenter?
Для случая универсальной проверки - это одна универсальная функция:

bool IsValid(string value)
{
return (value ? "").Trim.IsEmpty;
}

для различных проверок две разные функции

bool IsValid(int age)
{
return 0 <= age && age <= 150;
}
bool IsValid(string name)
{
return //сложная проверка валидности имени;
}

kokoc88

Термин mutable/immutable в этом обсуждении ты первый употребил. Я вообще ничего подобного не упоминал.
Согласен, я в какой-то момент начал смешивать stateless и mutable. По итогам обсуждения нам нужны stateful mutable модели для универсального рендеринга компонентов в представлении.

luna89

Можно сделать компонент MyInput
Такое решение не масштабируется.
Почему не масштабируется?

kokoc88

для различных проверок две разные функции
То есть, говоря простым языком, ты создаёшь класс, в котором есть mutable state для использования только внутри view; и связь с доменной моделью, которую ты используешь для вычисления свойств типа isValid и value. Такая модель создаётся для всех контролов.
Больше того, в примере от DDD достаточно попросить сделать следующее изменение. Если я ввёл буквы или знак минус, то текст не должен заменяться на NaN, а должен оставаться ровно таким, как я его ввёл и поддерживать редактирование с помощью del/backspace. При этом во время редактирования поле суммы должно содержать предыдущее корректное значение.

6yrop

Твой код выше - это простой пример. Если ты точно так же пишешь код в больших коммерческих проектах, то у меня для тебя плохие новости.
Именно на больших проектах и проявляется вред от лишнего mutable состояния. На маленьких проектах человек еще может отслеживать состояние, на больших проектах требуется помощь со стороны машины. В твоем коде можно переставить строчки местами
 
view.SetSum(model.Sum);
model.first = int.Parse(val);

и компилятор нас ни о чем не предупредит.
Если бы mutable состояния не было, то такого нельзя было бы сделать. Более того, даже если в большом проекте эти строчки разъехались бы по разным методам, компилятор всё равно бы следил за правильным порядком.

kokoc88

Именно на больших проектах и проявляется вред от лишнего mutable состояния.
Ещё раз повторю: если ты парсишь содержимое контролов каждый раз, когда тебе нужны данные, то у меня для тебя плохие новости. Хотя,

Dasar

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

luna89

Больше того, в примере от DDD достаточно попросить сделать следующее изменение. Если я ввёл буквы или знак минус, то текст не должен заменяться на NaN, а должен оставаться ровно таким, как я его ввёл и поддерживать редактирование с помощью del/backspace. При этом во время редактирования поле суммы должно содержать предыдущее корректное значение.
Я попозже напишу развернутый ответ

6yrop

паттерн Materialized View
Что за паттерн?
я только в Oracle этот термин знаю.

Dasar

я только в Oracle этот термин знаю.
фактически - это оно и есть, только для произвольного применения.
Materialized View бывает двух видов: материализация делается при каждом изменении, или только после изменений при первом запросе.
при втором варианте делается следующее:
есть исходный код со следующими свойствами:
state - изменяемое состояние
f(state) - тяжелая функция, которая рассчитывается на основе state
f - вызывается много чаще, чем меняется state
при проблемах с производительностью делается следующее:
в state добавляется changeTick, который увеличивается при каждом изменении
f оборачивается в materialized:

materialized(f)
{
var cache = null;
var changeTick = -1;
return (state)=>
{
if (changeTick == state.changeTick)
return cache;
changeTick = state.changeTick;
cache = f(state);
return cache;
}
}

6yrop

changeTick это же дополнительное mutable состояние.
Я иногда применяю вот такую функцию:
 
public static Func<T, TResult> Lazy<T, TResult>(Func<T, TResult> func, Func<T, T, bool> equalFunc)
{
var valueIsSet = false;
var cachedArg = default (T);
var cachedValue = default (TResult);
return arg => {
if (valueIsSet == false || !equalFunc(arg, cachedArg
{
cachedValue = func(arg);
cachedArg = arg;
valueIsSet = true;
}
return cachedValue;
};
}

public static Func<string, TResult> Lazy<TResult>(Func<string, TResult> func)
{
return Lazy(func, (x, y) => x == y);
}

public static Func<int, TResult> Lazy<TResult>(Func<int, TResult> func)
{
return Lazy(func, (x, y) => x == y);
}

Dasar

Я иногда применяю вот такую функцию:
Lazy оптимальнее для ситуаций, когда state - маленький(функция сравнения дешевая). На больших state-ах(функция сравнения дорогая) оптимальнее вариант с changeTick-ом.

6yrop

Lazy оптимальнее для ситуаций, когда state - маленький(функция сравнения дешевая). На больших state-ах(функция сравнения дорогая) оптимальнее вариант с changeTick-ом.
Т.е. мы приходим к тому, что характеристика "большой комерческий проект"(с) не является точной. Надо детально рассматривать структуру состояния в проекте.
По моему опыту сложное состояние встречается достаточно редко.

6yrop

Ещё раз повторю: если ты парсишь содержимое контролов каждый раз, когда тебе нужны данные, то у меня для тебя плохие новости. Хотя, OH, SHI....
Вариант с оптимизацией парсинга:
 
 
readonly Func<string, Option<int>> arg1Parse = Func.Lazy(_ => _.ParseInt32;
readonly Func<string, Option<int>> arg2Parse = Func.Lazy(_ => _.ParseInt32;

public Form1
{
arg1TextBox.TextChanged += delegate {
UpdateSum;
};
arg2TextBox.TextChanged += delegate {
UpdateSum;
};
UpdateSum;
}

void UpdateSum
{
sumLabel.Text = (from arg1 in arg1Parse(arg1TextBox.Text)
from arg2 in arg2Parse(arg2TextBox.Text)
select (arg1 + arg2).ToString.ValueOr("нецелочисленный аргумент");
}

Но это, конечно, ненужная оптимизация.

kokoc88

Вариант с оптимизацией парсинга:
... и с mutable состоянием.

Dasar

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

kokoc88

Mutable бывает разный - одни создают проблем, другие - нет.
Ага, давай ещё вводить градации mutable состояний и плодить больше обёрточного кода. Каждый int в отдельный класс с десятком свойств. Не удивительно, что потом появляются всякие высеры на тему убогости ООП. На деле mutable создаёт намного меньше проблем, чем лапша в control flow.

Dasar

Ага, давай ещё вводить градации mutable состояний и плодить больше обёрточного кода. Каждый int в отдельный класс с десятком свойств.
Как из первого следует второе?

kokoc88

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

6yrop

Так, что Шуреск уже завернул пару интов в две обёртки, у которых суммарно около 16 полей. Я уже молчу о том, что он создал коуплинг между моделью и представлением и нагородил кода, который тяжело прочитать.
Приведи свой не псевдо код, а полностью рабочий без эксепшенов код. Мой код полностью рабочий. Не вижу смысла сравнивать нерабочий псевдо код с полстью рабочим вариантом. Посмотрим у кого больше оберток. :grin:

kokoc88

Так, что Шуреск уже завернул пару интов в две обёртки, у которых суммарно около 16 полей.
Абсурдность подкрепляется тем, что вот тут наезд на перепутанный порядок следования строк, где компилятор нас не предупредит:
view.SetSum(model.Sum);
model.first = int.Parse(val);
А вот тут якобы спутать arg1Parse и arg2Parse никак не получится. Видимо, компилятор нас предупредит:
sumLabel.Text = (from arg1 in arg1Parse(arg1TextBox.Text)
from arg2 in arg2Parse(arg2TextBox.Text)
select (arg1 + arg2).ToString.ValueOr("нецелочисленный аргумент");

kokoc88

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

6yrop

Абсурдность подкрепляется тем, что вот тут наезд на перепутанный порядок следования строк, где компилятор нас не предупредит:view.SetSum(model.Sum);model.first = int.Parse(val);А вот тут якобы спутать arg1Parse и arg2Parse никак не получится. Видимо, компилятор нас предупредит:
Написать код первый раз не проблема. Один раз оттестировать и всё. Главная проблема при разработке ПО это внесение изменений так, чтобы ничего не поломать. Строки легко можно переставить при внесении изменений в уже работающий код. Ты же приводишь ошибку, которая отлавливается при первом тестировании и потом не возникает.

6yrop

Сначала ты приведи свой не псевдо код, а полностью рабочий, который можно скомпилировать, покрытый юнит тестами код.
Он уже в этом треде.
интеграторских решений,

никогда не работал в интеграторе

kokoc88

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

kokoc88

Он уже в этом треде.
Не вижу в этом треде кода, который можно скомпилировать и который покрыт юнит тестами.

6yrop

ты тоже приводи без юнит тестов

kokoc88

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

6yrop

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

6yrop

Не вижу смысла обсуждать подход, который не пригоден для сколько-нибудь серьёзной разработки. И код, который не пригоден для серьёзных коммерческих проектов.
Если бы ты еще аргументы написал, почему, на твой взгляд, он не пригоден.

kokoc88

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

6yrop

Т.е. аргумент такой, что я один на Земле.
Как это мимо тебя проходит не знаю, но о сокращении mutable состояния говорят многие.
Слушал какой-то подкаст, там чуваки даже из java признавались, что мол да java (еще до 8) не особо балует кратким синтаксисом для таких вещей, но когда они стали применять практику экономить mutable состояние, то жить стало намного проще.
Куча народу об этом говорит, вон тот же Хельсбер, я уже приводил ссылку.

kokoc88

Как это мимо тебя проходит не знаю, но о сокращении mutable состояния говорят многие.
Мимо тебя совершенно точно проходит, что ещё больше говорят о повышении читаемости кода, автоматическом тестировании и сокращении boilerplate. Кстати, ты всё время сливаешься в спорах про языки с динамической типизацией, т.к. скудоумие не даёт тебе понять, что для некоторых групп разработчиков читаемость более приоритетна, чем все плюсы статической типизации вместе взятые.

Dasar

arg1TextBox.TextChanged += delegate {
зачем пишешь слово 'delegate'?

6yrop

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

6yrop

А вот тут якобы спутать arg1Parse и arg2Parse никак не получится.
Я тут подумал над этой проблемой и решил посмотреть, а как с этой проблемой в твоем коде?
Оказалось, что там еще хуже, в два раза хуже. Легко можно написать так
 
 
void OnChanged1(string val)
{
model.second = int.Parse(val);
view.SetSum(sum);
}

А еще у тебя где-то вызов метода OnChanged1, там тоже можно перепутать 1 и 2. Т.е. у тебя в два раза больше мест, где можно неверно написать 2 вместо 1 и наоборот.
Таким образом, одно место, где можно легко сделать ошибку указал я. Второе место указал ты, и там у тебя тоже больше ошибок. Получаем счет 2:0 в мою пользу. Можешь дальше искать такие места в моем коде. Есть подозрение, что у тебя будет всё либо так же либо хуже. От того ты и не приводишь полный код.

6yrop

сокращении boilerplate
Приведи полный код. Посмотрим сколько строчек (и сколько boilerplate) у мега архитектора занимает сложение двух чисел. :grin: Напомню мой кране прозрачный, хорошо читаемый, без boilerplate .

kokoc88

Получаем счет 2:0 в мою пользу.
А теперь учитываем юнит тесты и читаемость и получаем +inf:2 в мою пользу. Самое смешное, что ты точно так же сливался в прошлом.

6yrop

Приведи код и юнит тесты.
Сливаешься ты.

kokoc88

Приведи код и юнит тесты.
Я не вижу в этом смысла, потому что не вижу смысла рассматривать твои кошмарные высеры в конктексте слова "код".

6yrop

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

kokoc88

Ты о чем?

6yrop

Где ты увидел объекты в этом ?

luna89

Больше того, в примере от DDD достаточно попросить сделать следующее изменение. Если я ввёл буквы или знак минус, то текст не должен заменяться на NaN, а должен оставаться ровно таким, как я его ввёл и поддерживать редактирование с помощью del/backspace. При этом во время редактирования поле суммы должно содержать предыдущее корректное значение.
Код

kokoc88

Код
В дискуссии с речь шла о создании универсального способа рендеринга для контрола.
Впрочем, в своём примере ты только подтвердил всё, о чём я писал выше. Тебе пришлось вынести редактируемое состояние контрола в state доменной модели, потому что типы данных доменной модели и модели контрола различаются. ( предлагал для этого использовать View Model.) А чтобы отобразить последнее правильное значение ты добавил mutable state с помощью lastGoodValue.

6yrop

 

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

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

kokoc88

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

6yrop

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

kokoc88

Сохраню, как образец хода мыслей, который по своей абсурдности не уступает женской логике.
Ололол, Шуреск расчехлил бота, чтобы ставить себе плюсики и минусовать неугодных. Сохраню как пример глубокой детской травмы и сублимации.

6yrop

Т.е. по существу сказать уже нечего? Пошли только обсуждения моей личности как часто ты это делаешь.
Простой пример с двумя полями оказался очень показательным. Реализация с лишним mutable состоянием начала просачиваться в видимые пользователю эффекты. Сначала появился термин "последнее корректное значение". Зачем появился? Ну да ладно, пока ничего плохого нет. Идем дальше. Оказалось, когда форма только открывается предыдущего значения нет. Надо дополнять правило. Дополняем: когда форма только открылась с пустыми слагаемыми на месте суммы отображаем 0 (или пусто). Идем дальше. Пользователь ввел целочисленные значения, а потом всё стер. На экране те же слагаемые, что и при открытии формы, а сумма другая. Не логично. Думаем, что же делать. Ага, надо в модели сделать поля nullable "int?". Пустые текстовые поля переводятся в null в полях модели. Получилось, что логика пользовательского интерфейса поменяла тип полей в модели. А декларировалось же, что модель вся такая из себя независимая.

kokoc88

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

apl13

Я могу только сказать тебе спасибо, за то, что поднял мне настроение на весь день.
Блин, завидую. Так подниматься настроением от таких унылых вещей.

6yrop

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

kokoc88

Очень рад, что являюсь главным героям твоих фантазий.
Вытри слёзки и поставь себе побольше плюсиков своим ботом. Чем больше поставишь, тем больше ты прав.

6yrop

Ты так комплексуешь забавно. :) Пора уже осваивать понятие функции. Это не сложно, ты справишься.

kokoc88

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

6yrop

Пример слишком маленький(мало функционала чтобы заметить отличие. Добавь, пожалуйста, для второго слагаемого замену текстового input-а на combobox(тег select) при нажатом checkbox-е и замену на input обратно при отжатом.В combo-е значения 0, 1, 5, 10. Уже введенное значение копируется из input-а в combo и обратно. Если в combo-е такого значения нет, то оно временно добавляется.
Думаю, что в ближайшее время напишу код для этого примера. Пока ответь, что значит "временно", когда оно исчезает, когда выбрали другое значение?

6yrop

Я не вижу в этом смысла, потому что не вижу смысла рассматривать твои кошмарные высеры в конктексте слова "код".
Как ты выражаешься "мои высеры" вовсе не мои, более того, они написаны даже wikipedia.

luna89

Думаю, что в ближайшее время напишу код для этого примера. Пока ответь, что значит "временно", когда оно исчезает, когда выбрали другое значение?
Если пользователь меняет в текстовом инпуте 13, а потом переключается на комбобокс, то в комбобоксе можно выбирать из значений 0, 1, 5, 10, 13

6yrop

Это понятно. Требуется уточнить смысл слова "временно". Было 13. Потом пользователь выбрал 5. 13 должно исчезнуть из списка или остаться?

luna89

Потом пользователь выбрал 5. 13 должно исчезнуть из списка или остаться?
Как я понял, остается там пока пользователь не переключится обратно с комбобокса в текстинпут.

Dasar

13 должно исчезнуть из списка или остаться?
Исчезнуть. Так код будет интереснее.

6yrop

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

6yrop

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

luna89

Я написал, модифицировав свой предыдущий код.
Я сделал компонент MegaInput, который рендерится как комбобокс или как текстинпут, в зависимости от чекбокса, и заменил input на MegaInput. Больше ничего не менял

6yrop

Пароль e=-mght__fdgfd7f54bbq,lpoenyxhhg
Код:
 
public partial class Form1 : Form
{
public Form1
{
InitializeComponent;

firstArgTextBox.TextChanged += delegate { RefreshResult; };
secondArgTextBox.TextChanged += delegate { RefreshResult; };
secondArgCheckBox.CheckedChanged += delegate {
RefreshSecondArgWidget;
RefreshResult;
};
secondArgComboBox.SelectedIndexChanged += delegate {
if (SelectedComboBoxItem == null || !SelectedComboBoxItem.Temporary)
RemoveTemporaryItem;
RefreshResult;
};
foreach (var item in new[] {0, 1, 5, 10})
secondArgComboBox.Items.Add(new ComboBoxItem(item.ToString false;
RefreshSecondArgWidget;
RefreshResult;
}

private void RefreshSecondArgWidget
{
secondArgTextBox.Visible = !secondArgCheckBox.Checked;
secondArgComboBox.Visible = secondArgCheckBox.Checked;
if (secondArgCheckBox.Checked)
{
RemoveTemporaryItem;
if (!string.IsNullOrEmpty(secondArgTextBox.Text
{
var item = secondArgComboBox.Items.Cast<ComboBoxItem>.SingleOrDefault(_ => _.Text == secondArgTextBox.Text);
if (item == null)
{
item = new ComboBoxItem(secondArgTextBox.Text, true);
secondArgComboBox.Items.Add(item);
}
secondArgComboBox.SelectedItem = item;
}
else
secondArgComboBox.SelectedItem = null;
}
else
secondArgTextBox.Text = SelectedComboBoxItem == null ? "" : SelectedComboBoxItem.Text;
}

private ComboBoxItem SelectedComboBoxItem
{
get { return (ComboBoxItem) secondArgComboBox.SelectedItem; }
}

private void RemoveTemporaryItem
{
var temporaryItem = secondArgComboBox.Items.Cast<ComboBoxItem>.SingleOrDefault(_ => _.Temporary);
if (temporaryItem != null) secondArgComboBox.Items.Remove(temporaryItem);
}

private void RefreshResult
{
sumLabel.Text = (from firstArg in firstArgTextBox.Text.ParseInt32
from secondArg in (secondArgCheckBox.Checked
? SelectedComboBoxItem == null ? new Option<string> : SelectedComboBoxItem.Text
: secondArgTextBox.Text).SelectMany(_ => _.ParseInt32
select (firstArg + secondArg).ToString.ValueOr("нецелочисленный аргумент");
}

private class ComboBoxItem
{
public readonly string Text;
public readonly bool Temporary;

public ComboBoxItem(string text, bool temporary)
{
Text = text;
Temporary = temporary;
}

public override string ToString
{
return Text;
}
}
}

Пока твой код посмотрел не полностью (кофескрипт читаю с трудом). Я так понимаю lift это такая своеобразная работа с maybe монадой?

luna89

Пока твой код посмотрел не полностью (кофескрипт читаю с трудом). Я так понимаю lift это такая своеобразная работа с maybe монадой?
Вроде стандартная функция, тут хорошее объяснение.

6yrop

У тебя в первом варианте состояние было с целыми числами, в последнем варианте состояние со строками. Прокомментируй это изменение.

6yrop

Пример слишком маленький(мало функционала чтобы заметить отличие.

Реализовал я твой пример.
Небольшое преимущество реакта есть, но оно очень небольшое. Можно пример чтоб прям ух какая большая разница была? Если, как говорит , там квадратичная зависимость против линейной на реакте, то, наверное, не сложно придумать так, чтобы на реакте потребовалось 10 условных единиц усилий, а мне потребовалось 100 усилий.
Повторюсь, реакт оборачивает стандартный DOM, поэтому надо понимать насколько оправдана такая обертка.

Dasar

Небольшое преимущество реакта есть, но оно очень небольшое.
Ты используешь более оптимальный подход, чем классический. Отсюда и не очень большая разница с react-ом.
По классификации ниже ты используешь второй подход(слабо-связанный).
Классический сильно-связанный подход - Presenter(предыдущее отображение, ViewModel, Model
слабо связанный:
  - новое отображение = Presenter(ViewMode, Model)
  - diff = Diff(текущее отображение, новое отображение)
 - Apply(предыдущее отображение, diff)
Основное отличие твоего подхода от React-а: ты делаешь в ручную diff и его применение, а React делает это автоматически.
Соответственно, в react-е кода будет меньше только на эту разницу

6yrop

Я тут подумал над этой проблемой и решил посмотреть, а как с этой проблемой в твоем коде?
Оказалось, что там еще хуже, в два раза хуже. Легко можно написать так
я тут еще немного подумал над этим, оптимизацию парсинга можно сделать с помощью такого общего метода:
 
 
public static class TextBoxExtensions
{
public static Option<int> GetInt32(this TextBox it)
{
const string key = "ParseInt32";
var data = it.GetData(key);
Func<string, Option<int>> parse;
if (!data.HasValue)
{
parse = Func.Lazy(_ => _.ParseInt32;
it.SetData(key, parse);
}
else
parse = (Func<string, Option<int>>) data.Value;
return parse(it.Text);
}
}

public static class ControlExtensions
{
public static Option<object> GetData(this Control it, string key)
{
object value;
if (GetDictionary(it).TryGetValue(key, out value return value;
return new Option<object>
}

public static void SetData(this Control it, string key, object value)
{
GetDictionary(it)[key] = value;
}

private static Dictionary<string, object> GetDictionary(Control it)
{
if (it.Tag == null)
{
var dictionary = new Dictionary<string, object>
it.Tag = dictionary;
return dictionary;
}
else
return (Dictionary<string, object>) it.Tag;
}
}

Оптимизированный код выглядит следующим образом:
 
 
public Form1
{
firstArgTextBox.TextChanged += delegate { RefreshSum; };
secondArgTextBox.TextChanged += delegate { RefreshSum; };
RefreshSum;
}

void RefreshSum
{
sumLabel.Text = (from firstArg in firstArgTextBox.GetInt32
from secondArg in secondArgTextBox.GetInt32
select (firstArg + secondArg).ToString.ValueOr("нецелочисленный аргумент");
}

Теперь first и second не перепутаешь. А теперь ты попробуй у себя устранить проблему, на которую ты сам указал. :smirk:

6yrop

Добавить третье слагаемое без длительных мысленных усилий в твоём случае не представляется возможным.
В чем проблема?
 
public Form1
{
firstArgTextBox.TextChanged += delegate { RefreshSum; };
secondArgTextBox.TextChanged += delegate { RefreshSum; };
thirdArgTextBox.TextChanged += delegate { RefreshSum; };
RefreshSum;
}

void RefreshSum
{
sumLabel.Text = (from firstArg in firstArgTextBox.GetInt32
from secondArg in secondArgTextBox.GetInt32
from thirdArg in thirdArgTextBox.GetInt32
select (firstArg + secondArg + thirdArg).ToString.ValueOr("нецелочисленный аргумент");
}

kokoc88

В чем проблема?
Если ты сам не понимаешь, то боюсь, что тебе это уже никто не объяснит. Так и придётся мучаться до смерти твоему гению среди серости и обыденности.

6yrop

Если ты сам не понимаешь, то боюсь, что тебе это уже никто не объяснит. Так и придётся мучаться до смерти твоему гению среди серости и обыденности.
и работают с maybe монадой аналогичным образом.

kokoc88

и работают с maybe монадой аналогичным образом.
Жалкие попытки повторить за истиным творцом!

Dasar

    sumLabel.Text = (from firstArg in firstArgTextBox.GetInt32
        from secondArg in secondArgTextBox.GetInt32
        from thirdArg in thirdArgTextBox.GetInt32
        select (firstArg + secondArg + thirdArg).ToString.ValueOr("нецелочисленный аргумент");
Имхо, вот так будет понятнее

Func<int, int> Add = (i,j)=>i+j;

sumLabel.Text = firstArgTextBox.GetInt32
.F(Add, secondArgTextBox.GetInt32
.F(Add, thirdArgTextBox.GetInt32
.OrDefault("нецелочисленный аргумент");


static class OptionHlp
{
public static Option<TTarget> F<TArg1, TArg2, TTarget>(this Option<TArg1> value1, Func<TArg1, TArg2, TTarget> f, Option<TArg2> value2)
{
if (value1.HasValue && value2.HasValue)
return f(value1, value2);
return Option.Empty;
}
}

6yrop

Имхо, вот так будет понятнее
а если надо
  
2*firstArg + decimal.Round(secondArg*thirdArg/100, MidpointRounding.AwayFromZero)

Dasar


2.ToOption.F(Mul, firstArg)
.F(Add, secondArg.F(Mul, thirtArg).F(Div, 100).F(decimal.Round, MidpointRounding.AwayFromZero

ps
Еще есть вот такой вариант:

var res = OptionProcess=>2*firstArgTextBox.GetInt32.Mark
+ decimal.Round(secondArgTextBox.GetInt32.Mark*thirdArgTextBox.GetInt32.Mark/100, MidpointRounding.AwayFromZero;


static class OptionHlp
{
//fake-овая функция. Маркирует выражения для функции OptionEvaluate
static T Mark<T>(this Option<T> option)
{
return option.Value;
}
static Func<Option<T>> OptionProcess<T>(Expression<Func<T>> f)
{
//перекомпиливает функцию f. Ищет подвыражения помеченные Mark и выносит их в качестве аргументов.
//Вышеприведенное выражение будет перетранслировано в выражение вида:
return =>
{
var arg1 = firstArgTextBox.GetInt32;
var arg2 = secondArgTextBox.GetInt32;
var arg3 = thirtArgTextBox.GetInt32;
if(!arg1.HasValue || !arg2.HasValue || !arg3.HasValue)
return Option.Empty;
return 2*arg1 + decimal.Round(arg2*arg3/100, MidpointRounding.AwayFromZero);
}
}
}

6yrop

Давай посмотрим на это с точки зрения нового человека на проекте. Или с точки зрения человека, который вернулся к этому коду через год. Monadic comprehension syntax это часть языка C#. Человек пишет на C#, поэтому естественно, что он должен знать его синтаксис. В твоем варианте надо вникать в эти дополнительные специализированные библиотечные функции.

6yrop

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

Dasar

Monadic comprehension syntax это часть языка C#.
Имхо, в Code style стоит прописывать явный запрет на его использование. Данный синтаксис - мертворожденный костыль.И сделан был только для того, чтобы не испугать тех, кто переходил с написания sql-запросов на linq-запросы.
>> Monadic comprehension syntax
Если он задумывался как Monadic, то нафига в него затащили sql-ный синтаксис, который там ни к селу, ни к городу?

Dasar

Ты можешь дать ссылки на то, что ты называешь "классический"?
MV, MVC, MVVM, MVP

6yrop

Где можно прочесть их классическое описание?

Dasar

Где можно прочесть их классическое описание?
вики, msdn, Fowler

6yrop

На Фаулера я уже давал в этом треде ссылку, это кошмар.
Разных архитектур много, более того это список открытый:
 

Conclusion
The Model-View-Controller, Model-View-Presenter, and Presentation-Abstraction-Control patterns are similar in many ways, but have each evolved to address slightly different concerns. By becoming familiar with these patterns and other related architecture models, developers and architects will be better equipped in choosing an appropriate solution in their next design endeavor, or possibly in the creation of future architecture design patterns.
http://aspiringcraftsman.com/2007/08/25/interactive-applicat...
 

Разумные разработчики используют whatever works for you.
Таким образом, классики нет, есть множество конкурирующий подходов. Применение в данном случае слова "классический" это чистой воды софистика.

6yrop

И сделан был только для того, чтобы не испугать тех, кто переходил с написания sql-запросов на linq-запросы.
Причина глубже. Стандартный монадный синтаксис такой:
 
m1.bing(x1 => m2.bind(x2 => m3.bind(x3 => someFunction(x1, x2, x3

Но выяснилось, что вывод типов вместе с перегрузкой методов дают экспоненциальную сложность для компилятора.
Кто-то из команды C# догадался, что это можно переписать вот так:
 
m1.bing(x1 => m2.bind(x2 => new {x1, x2}.bind(temp => m3.bind(x3 => someFunction(temp.x1, temp.x2, x3

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

Если он задумывался как Monadic, то нафига в него затащили sql-ный синтаксис, который там ни к селу, ни к городу?

Если не обращать внимание на преемственность с другими языками, то какая разница "bind" или "SelectMany"? В Java назвали flatMap. К названиям можно привыкнуть, это не big deal.

6yrop

вики, msdn, Fowler
Вот вспомнил у Фаулера есть то, что я использую Flow Synchronization.
То есть у меня тоже классика :grin:

6yrop

Flow Synchronization
Собственно в control flow и вся суть. Язык с compile time проверками и с развитым инструментарием по обработке исходного кода позволяет без проблем писать и развивать control flow, который не становиться сложным при росте количества UI элементов.
После освоения языка в достаточной мере многие задачи распадаются на мелкие задачи естественным образом. :)

6yrop

... и с mutable состоянием.
Да, с mutable состоянием, но это состояние полностью инкапсулировано в функции Lazy. Внешний интерфейс совпадает с интерфейсом без изменяемого стояния.
В твоем коде такой инкапсуляции нет. У тебя выставлены наружу и гетер model.Sum и сетеры model.first, model.second. Программисту нужно помнить, что происходит в гетере model.Sum, и на основании этого знания вызывать model.Sum только после сетеров model.first, model.second. В коде без изменяемого состояния эти знания представлены в формальной структуре кода.

kokoc88

Да, с mutable состоянием, но это состояние полностью инкапсулировано в функции Lazy. Внешний интерфейс совпадает с интерфейсом без изменяемого стояния.
Каждый байт, выполняемый процессором, должен превратиться в отдельный класс. Такая задача подсилу только во истину великим.

6yrop

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

Dasar

Количество вложенных лямбд теперь не растет.
Растёт кол-во пересоздаваемых объектов, что убивает производительность и напрямую, и косвенно (через повышенную нагрузку gc)

Dasar

К названиям можно привыкнуть, это не big deal.
Cам синтаксис чужеродный для C# и в этом проблема. Это всё равно, что brainfuck затащить в C#, писать и на нём, конечно, можно, но это убъект производительность программиста из-за множества переключений контекста.

6yrop

нужны цифры, чтобы понять существенно это или нет. Подозреваю, что в Москве не пишут на .NET проектов, в которых это сколько-нибудь существенно.

6yrop

Cам синтаксис чужеродный для C# и в этом проблема. Это всё равно, что brainfuck затащить в C#, писать и на нём, конечно, можно, но это убъект производительность программиста из-за множества переключений контекста.
Не говори ерунды, LINQ пользуются, программисты знают этот синтаксис.

luna89

Повторюсь, реакт оборачивает стандартный DOM
На стандартном DOM далеко не уедешь. Нужна абстракция виджета и возможность строить дерево виджетов. Нужны аналоги реактовских колбэков componentDidMount и componentWillUnmount, иначе невозможно создавать виджеты, которым надо, например, навесить какой-то глобальный обработчик событий и убрать его при удалении.
Затем, в твоем коде видны зачатки проблем с утечками памяти. Есть виджет A, на события в нем подписан виджет B. Когда виджет B удаляется, то ссылка на него остается висеть в виджете A. Понимание этой проблемы на порядок сложнее, чем все API реакта вместе взятое.

luna89

У тебя в первом варианте состояние было с целыми числами, в последнем варианте состояние со строками. Прокомментируй это изменение.
Был еще второй вариант, в котором я добавил валидацию.

6yrop

На стандартном DOM далеко не уедешь. Нужна абстракция виджета и возможность строить дерево виджетов. Нужны аналоги реактовских колбэков componentDidMount и componentWillUnmount, иначе невозможно создавать виджеты, которым надо, например, навесить какой-то глобальный обработчик событий и убрать его при удалении.
Затем, в твоем коде видны зачатки проблем с утечками памяти. Есть виджет A, на события в нем подписан виджет B. Когда виджет B удаляется, то ссылка на него остается висеть в виджете A. Понимание этой проблемы на порядок сложнее, чем все API реакта вместе взятое.
У меня нет утечек памяти. Описанный сценарий к моему коду не имеет ни малейшего отношения. Ни каких зачатков нет.
На стандартном DOM далеко не уедешь. Нужна абстракция виджета и возможность строить дерево виджетов. Нужны аналоги реактовских колбэков componentDidMount и componentWillUnmount, иначе невозможно создавать виджеты, которым надо, например, навесить какой-то глобальный обработчик событий и убрать его при удалении.
Затем, в твоем коде видны зачатки проблем с утечками памяти. Есть виджет A, на события в нем подписан виджет B. Когда виджет B удаляется, то ссылка на него остается висеть в виджете A. Понимание этой проблемы на порядок сложнее, чем все API реакта вместе взятое.
Отличная логика. Нам нужна абстракция виджета. Если виджеты подписываются на события друг друга, то получается утечка памяти. Нужно решение этой проблемы: componentDidMount, componentWillUnmount.
В начале проблем не было. Ввели абстракцию. Получили проблему. Потребовалось решение проблемы. А может дырявая абстракция? И может она нам не очень нужна?
Самое время напомнить:
 

... выводить нужный уровень абстракции по требованию
 

Благодаря постепенному выводу абстракций, если в исходном коде не было проблем (например, утечек памяти то и в конечном коде таких проблем не будет (если при выводи мы ничего не добавляли).
На стандартном DOM далеко не уедешь.
Смотря на чем ехать. Если язык позволяет легко управлять потоком исполнения в исходном коде, то ехать можно далеко и без проблем. В языке уже есть абстракция для построения правильного потока исполнения и для вывода правильных абстракций.
Абстракции типа реакта плохи тем, что практически закрывают нижележащий уровень (DOM).
VSCode построен с помощью TypeScript и без реакта. Реакт там не подойдет из-за большого оверхеда на построение виртуального dom и вычисления diff. А TypeScript справился с построением потока выполнения и без абстракций реакта.

6yrop

На стандартном DOM далеко не уедешь. Нужна абстракция виджета и возможность строить дерево виджетов. Нужны аналоги реактовских колбэков componentDidMount и componentWillUnmount, иначе невозможно создавать виджеты, которым надо, например, навесить какой-то глобальный обработчик событий и убрать его при удалении.
Затем, в твоем коде видны зачатки проблем с утечками памяти. Есть виджет A, на события в нем подписан виджет B. Когда виджет B удаляется, то ссылка на него остается висеть в виджете A. Понимание этой проблемы на порядок сложнее, чем все API реакта вместе взятое.
Про утечку памяти. В стандартном походе UI элементы не подписываются на события друг друга. На события от UI элементов подписывается третий объект X. Поэтому UI элементы можно просто удалять без утечки памяти.
События это, грубо говоря, список указателей на функции. Это mutable состояние. Если мы просто при старте формы подписываемся на события изменчивая природа состояния почти не проявляется, это близко к readonly переменным. Если же архитектура требует подписки и отписки от событий, тогда изменяемое состояние проявляет свои худшие стороны.
Ты привел пример как ООП подход приводит к плохому результату. На первый взгляд кажется "естественным" ввести понятие виджета. А в коде оно создает проблему, которой не было. Поэтому ООП, которое пропагандирует брать абстракции "из объектов реального мира", приводит к
accidental complexity. Лучше выводить абстракции из требований к продукту и из текста кода.

luna89

У меня нет утечек памяти. Описанный сценарий к моему коду не имеет ни малейшего отношения. Ни каких зачатков нет.
У всех есть, а у тебя нет. Может быть ты просто не осознаешь проблему?
Как ты собрался реализовывать componentDidMount/componentWillUnmount ты тоже не объяснил. Все js фреймворки пытались его реализовать с переменным успехом.

6yrop

У всех есть, а у тебя нет. Может быть ты просто не осознаешь проблему?
Я ее осознаю ровно так как описано в первом ответе http://stackoverflow.com/a/4526840
 
Как ты собрался реализовывать componentDidMount/componentWillUnmount ты тоже не объяснил.

Мне они не нужны.
 

Все js фреймворки пытались его реализовать с переменным успехом.
 

Раньше IE не мог полностью отслеживать когда ссылки на объект пропадают и из DOM и из js машины. Это делали фрейворки. Сейчас это, вроде, уже история.
Приведи код с удаление элемента на ванильном js или jquery, я и покажу что ты не до конца осознаешь.

6yrop

Ты говоришь, что утечки памяти возникает при использовании такого кода:
 
 
subscribe {
a.E1 += b.M1;
}
subscribe;
...
//удаляем b, создаем новый и для нового объекта подписываемся
b = new B; //ссылка на старый экземпляр (который был в b) осталась в E1
subscribe;

Если подписываться через третий объект, то проблемы не возникает:
 
 
a.E1 += => b.M1;
...
//удаляем b, создаем новый
b = new B; //старый экземпляр (который был в b) удаляется сборщиком мусора

Dasar

//удаляем b, создаем новый
b = new B; //старый экземпляр (который был в b) удаляется сборщиком мусора
Не будет удаление старого b. У a осталась ссылка на него.

6yrop

Не будет удаление старого b. У a осталась ссылка на него.
Не обманывай :) :
 
class Program
{
static void Main(string[] args)
{
var program = new Program;
program.a.OnE1;
program.b = new B(2);
GC.Collect;
Thread.Sleep(1000);
Console.WriteLine("GC.Collect");
program.a.OnE1;
}

public Program
{
a.E1 += => b.M1;
}

public A a = new A;

public B b = new B(1);
}

class A
{
public event Action E1;

public void OnE1
{
if (E1 != null) E1;
}
}

class B
{
private readonly int i;

public B(int i)
{
this.i = i;
}

public void M1
{
Console.WriteLine("M1" + i);
}

~B
{
Console.WriteLine("~B" + i);
}
}

Результат:
 
M11
~B1
GC.Collect
M12
~B2

Dasar

Согласен. Не будет утечки, если b это поле и происходит переприсваивание именно его.
ps
Сомневаюсь, что возможно выстроить реальный интерфейс без отписывания от событий.

6yrop

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

Dasar

Отписывание от событий используется либо для обхода косяков в используемых компонентах, либо это говнокод.
В сложном интерфейсе появляется инкапсуляция, динамика и временность. Как предлагаешь это реализовывать без отписывания от событий?
Инкапсуляция - UI разделен на части, которые не знают напрямую ничего друг о друге
Динамика - интерфейс меняется - перестраивась в ответ на действия пользователя
Временность - часть элементов проявляется в UI только временно: анимация, отображение текущей операции и т.д.
В твоём примере все эти три пункта нарушаются:
- инкапсуляция: Program знает о поведении всех частей UI
- динамика: интерфейс не меняется
- временность: реагирование на a.E1 всегда одно и тоже в течении всего отображения UI

6yrop

Всё из перечисленного можно показать на маленьких примерах. Код в студию.

Dasar

на форме есть контролы A
Контролы A добавляются/удаляются пользователем
Контролы A для своей работы постоянно выполняют "тяжелую" функцию.
Контролы A отключают выполнение "тяжелой" функции, когда форма свернута, подписываясь для этого на события формы

6yrop

Я же просил код. :crazy: Без кода сейчас еще на десяти страницах воды нальем.

подписываясь для этого на события формы

подписаться на событие формы один раз
 
form.Minimized += => foreach(var aControl in AControls) aControl.ОтключитьТяжелуюФункцию;

Dasar

подписаться на событие формы один раз
Кто это делает? Если сама форма, то нарушается инкапсуляция. Форма тогда знает о том, что бывают контролы с тяжелой функцией.
Что такое AControls? Все контролы A регистрируются в static-коллекции?

6yrop

Ты пишешь "Контролы A добавляются/удаляются пользователем"
Куда они добавляются?
Код бы всё разъяснил сразу.

Dasar

Куда они добавляются?
куда-то в дерево контролов. (до этого тобой декларировался принцип, что не стоит состояние хранить где-то еще, если оно и так уже хранится в самих контролах)

6yrop


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

Ты, действительно, не знаешь как сделать эту инкапсуляцию без изменяемого состояния?
У тебя в контрол A передается форма. Пусть это происходит в конструкторе (если не в конструкторе, а в сетере, то всё аналогично):
 
public A(Form form) {
form.Minimized += => { ... }
};

Инкапсуляция делается следующим образом:
 
public static Func<A> ACreator(Form form) {
form.Minimized += => foreach(var aControl in AControls) aControl.ОтключитьТяжелуюФункцию;
return => ...;
};

private A {
}


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

Тогда для AControls используются селекторы, например, по css классу.

6yrop

тестами код

Тесты и мутабельность это не связанные вещи. На imutable коде тесты хорошо пишутся.

luna89

Тем временем, Facebook заопенсорсил многоязычную интегрированную среду разработки Nuclide. В качестве фронтенда она использует Atom - редактор на основе HTML5, а в качестве бэкенда - движок для статического анализа кода Infer.
Это еще один шаг на пути к созданию Facebook Stack (так, думаю, это назовут в будущем). Еще немного, и можно будет начинать разрабатывать кроссплатформенное приложение целиком на технологиях Facebook. Пишем код на FlowType в среде Nuclide. В качестве UI фреймворка используем React, сразу в разы облегчая себе разработку Android и IOS приложений благодаря React Native. Общаемся с сервером по протоколу GraphQL.
В принципе, фейсбуку осталось только заопенсорсить серверную технологию. Я мечтаю о СУБД, из коробки поддерживающей GraphQL по HTTP и вебсокетам, с хранимыми процедурами на javascript.
Можно сказать, что фейсбук более чем достойно отвечает на вызов, обозначенный Шуриком в этом разделе. Действительно, простые разработчики устали от постоянной фрустрации, вызванной фрагментированностью и отсутствием целостного видения при разработке под такие платформы, как, например, Java и .Net.

kokoc88

Действительно, простые разработчики устали от постоянной фрустрации, вызванной фрагментированностью и отсутствием целостного видения при разработке под такие платформы, как, например, Java и .Net.
Простые разработчики устали получать 1 звезду от пользователей, у которых тормозят не native приложения. Это к истории с Facebook, да.

luna89

Простые разработчики устали получать 1 звезду от пользователей, у которых тормозят не native приложения. Это к истории с Facebook, да.
Не понял, о чем ты. У тебя какие-то претензии к продуктам или библиотекам фейсбука?

kokoc88

Не понял, о чем ты. У тебя какие-то претензии к продуктам или библиотекам фейсбука?
Рейтинг приложения Facebook, написанного Facebook на HTML5, был очень низким из-за того, что оно дико тормозило. Пока они не переписали всё на native.

luna89

Рейтинг приложения Facebook, написанного Facebook на HTML5, был очень низким из-за того, что оно дико тормозило.
Да, HTML5 приложения проигрывают нативным. Именно поэтому фейсбук создал React Native, который не использует HTML5

6yrop

Я мечтаю о СУБД, из коробки поддерживающей GraphQL по HTTP и вебсокетам, с хранимыми процедурами на javascript.
Какая модель данных у этой СУБД?

kokoc88

Именно поэтому фейсбук создал React Native, который не использует HTML5
Да, но это тоже не Native. Я более чем уверен, что все дифирамбы утихнут на пару-тройку лет, когда выйдут реальные приложения, которые будут тормозить или плохо работать. Не верится, что Facebook в чём-то принципиально отличается от разработчиков Native компонентов в Google и Apple.

luna89

Да, но это тоже не Native. Я более чем уверен, что все дифирамбы утихнут на пару-тройку лет, когда выйдут реальные приложения, которые будут тормозить или плохо работать. Не верится, что Facebook в чём-то принципиально отличается от разработчиков Native компонентов в Google и Apple.
А в чем принципиальное отличие от Native приложений? Виджеты используются нативные, можно писать виджеты на Java / ObjectiveС и использовать их в React.Native приложении. Весь код бизнес-логики, взаимодействия с сервером и часть вью-логики типа "при нажатии на кнопку перейти на другой экран" оказывается кроссплатформенной.

6yrop

Кстати, ты всё время сливаешься в спорах про языки с динамической типизацией, т.к. скудоумие не даёт тебе понять, что для некоторых групп разработчиков читаемость более приоритетна, чем все плюсы статической типизации вместе взятые.
Статический анализ кода компилятором/инструментами, не использование лишнего mutable состояния — всё это является средствами улучшения читаемости кода.
Довольно странно рассматривать отдельно читаемость и статический анализ. Закрадывается подозрение, что ты не понимаешь и поэтому не используешь все преимущества статического анализа.

kokoc88

Статический анализ кода компилятором/инструментами, не использование лишнего mutable состояния — всё это является средствами улучшения читаемости кода.
Куколка моя, я смотрю, ты перечитал весь тредик, чтобы рассказать всем о том, что я не понимаю? Видишь ли, вот этому кодику статический анализ с читаемостью как бы не помогает:
        public static Func<object, QWriter, TemplateBase> BuildQFragment(Type type, string text,
[CallerLineNumber] int lineNumber = 0, [CallerFilePath] string filePath = "")
{
//TODO: теперь имя класса может быть постоянным, поскольку эмитем в отдельную сборку. Сделать это.
var template = "T" + Guid.NewGuid.ToString("N");
var host = new RazorEngineHost(new CSharpRazorCodeLanguage {
GeneratedClassContext = new GeneratedClassContext(
executeMethodName: GeneratedClassContext.Default.ExecuteMethodName,
writeMethodName: GeneratedClassContext.Default.WriteMethodName,
writeLiteralMethodName: GeneratedClassContext.Default.WriteLiteralMethodName,
writeToMethodName: WriteToMethodName,
writeLiteralToMethodName: WriteLiteralToMethodName,
templateTypeName: typeof (QFragment).FullName,
defineSectionMethodName: GeneratedClassContext.Default.DefineSectionMethodName
DefaultBaseClass = typeof (TemplateBase).FullName,
DefaultClassName = template
};
var razorConfiguration = RazorConfigurationHelper.Get(filePath, lineNumber);
foreach (var namespaceImport in razorConfiguration.Item1)
host.NamespaceImports.Add(namespaceImport);

var engine = new RazorTemplateEngine(host);
GeneratorResults razorResult;
using (var reader = new StringReader(text
razorResult = engine.GenerateCode(reader, className: null, rootNamespace: null,
sourceFileName: GetQueryPath(filePath, lineNumber;
if (!razorResult.Success)
{
var error = razorResult.ParserErrors[0];
throw new ApplicationException(
String.Format("Razor parsing error: '{3}' (Caller line number {4}, Ln {0}, Col {1}) {2}",
error.Location.LineIndex + 1, error.Location.CharacterIndex + 1, error.Message,
filePath, lineNumber;
}
StringWriter stringWriter;
using (stringWriter = new StringWriter
using (var codeProvider = new CSharpCodeProvider
codeProvider.GenerateCodeFromCompileUnit(
razorResult.GeneratedCode, stringWriter, new CodeGeneratorOptions;

var propertyInfos = type.GetProperties.Select(_ => new {
_.Name,
_.PropertyType,
SetterName = "." + _.Name
}).ToList;
if (propertyInfos.All(_ => _.Name != "p"
propertyInfos.Add(new {
Name = "p",
PropertyType = type,
SetterName = ""
});

var types = new Dictionary<Assembly, string>
var syntax = GetTypeSyntax(type, types);
var typeSyntaxs = propertyInfos.Select(_ => GetTypeSyntax(_.PropertyType, types.ToList;

var compilationUnitSyntax = SyntaxTree.ParseText(stringWriter + @"
namespace Razor
{
" + String.Join(@"
", types.Select(_ => " extern alias " + _.Value + ";" + @"

public partial class " + template + @"
{
internal static " + typeof (TemplateBase).FullName + @" M58a3d6d024dc47c6a692e20e29210b89(object arg, " +
typeof (QWriter).FullName + @" writer) {
var arg2 = (System.Object)arg;
var result = new " + template + @"(writer);
" + String.Join(@"
", propertyInfos.Select(_ => " result." + _.Name + " = arg2" + _.SetterName + ";" + @"
return result;
}

public " + template + @"(" + typeof (QWriter).FullName + @" builder): base(builder) {}

" + String.Join(@"
", propertyInfos.Select(_ => " internal System.Object " + _.Name + ";" + @"
}
}
").GetRoot;
var descendantNodes = compilationUnitSyntax.ChildNodes.Skip(1).First.DescendantNodes;
var syntaxTree = SyntaxTree.CreateCompilationUnitSyntax) new Rewriter(
descendantNodes.OfType<MethodDeclarationSyntax>.First.DescendantNodes
.OfType<CastExpressionSyntax>.First
syntax,
descendantNodes.OfType<FieldDeclarationSyntax>.Select(
_ => _.DescendantNodes.OfType<VariableDeclarationSyntax>.Single
.Take(typeSyntaxs.Count).SelectdeclarationSyntax, i) => new {
declarationSyntax,
typeSyntax = typeSyntaxs[i]
}).ToDictionary(_ => _.declarationSyntax, _ => _.typeSyntax)
).Visit(compilationUnitSyntax;
var compilation = Compilation.Create(DynamicMaterializer.AssemblyName.Name,
new CompilationOptions(OutputKind.DynamicallyLinkedLibrary
new[] {syntaxTree},
types.Select(_ => new MetadataFileReference(_.Key.Location,
new MetadataReferenceProperties(alias: _.Value
.Concat(razorConfiguration.Item2
.Select(_ => new MetadataFileReference(_.Location;
var className = "Razor." + template;
CommonEmitResult emitResult;
Func<Type> typeFunc;
if (!Debugger.IsAttached)
{
emitResult = compilation.Emit(DynamicMaterializer.ModuleBuilder);
typeFunc = => DynamicMaterializer.ModuleBuilder.GetType(className);
}
else
{
MemoryStream stream;
MemoryStream pdbStream;
using (stream = new MemoryStream
using (pdbStream = new MemoryStream
emitResult = compilation.Emit(stream, pdbStream: pdbStream);
typeFunc = => Assembly.Load(stream.GetBuffer pdbStream.GetBuffer.GetType(className);
}

foreach (var diagnostic in emitResult.Diagnostics)
{
var lineSpan = diagnostic.Location.GetLineSpan(usePreprocessorDirectives: true);
throw new ApplicationException(
string.Format("Build error: '{3}' (Caller line number {4}, Ln {0}, Col {1}) {2}",
lineSpan.StartLinePosition.Line + 1,
lineSpan.StartLinePosition.Character + 1,
diagnostic.Info.GetMessage
filePath, lineNumber;
}

return (Func<object, QWriter, TemplateBase>) typeFunc
.GetMethod("M58a3d6d024dc47c6a692e20e29210b89", BindingFlags.Static | BindingFlags.NonPublic)
.CreateDelegate(typeof (Func<object, QWriter, TemplateBase>
}

luna89

Вчера потестил Nuclide. Интеграция с flowtype не заработала, попытался отдебажить проблему, но быстро это не удалось сделать. Atom - просто оболочка над браузером, там есть такие же Developer Tools, можно дебажить Atom в самом себе, получается self-hosted debugging. На скриншоте можно увидеть современный джаваскрипт код - с async, await и типами.

6yrop

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

6yrop

Видишь ли, вот этому кодику статический анализ с читаемостью как бы не помогает:
У тебя низкие навыки чтения чудого кода. Учись читать.

kokoc88

У тебя низкие навыки чтения чудого кода. Учись читать.
Шурика кода не читателя, Шурика кода писателя. Но вообще, да, проблемы в твоём коде нет, продолжай так думать, а то ещё узнаешь правду и сбросишься с ГЗ. Вот уже опечатки по Фрейду делаешь.

6yrop

Назовешь проблемы, будет повод думать иначе. А пока в твоих постах только фапанье на мой проект.

kokoc88

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

Dasar

Мне тоже многое не нравится в стиле оформления кода Shurick-а, но это не отменяет необходимость в задачах генерации кода, статической валидации кода и написания такого кода, который удобен для статической валидации.
Эти задачи решает и твой работодатель - Microsoft. Entity Framework предоставляет статически валидируемый доступ к БД.
Интересно обсудить, какие бы ты использовал подходы, и какой бы ты рекомендовал стиль код для решения задач:
- генерация dbml из БД
- генерация кода на основе dbml
- исполнение EF-запросов
- проверки того, что в этих генерациях минимизированы ошибки

6yrop

Интересно обсудить, какие бы ты использовал подходы, и какой бы ты рекомендовал стиль код для решения задач:- генерация dbml из БД- генерация кода на основе dbml- исполнение EF-запросов- проверки того, что в этих генерациях минимизированы ошибки
Ты спрашиваешь так как будто не заешь ответа. ТЕСТЫ!
Для пастуха тимлида идеальный подход. Возникли проблем? Значит написали мало тестов. Пишите, пишите, обезьянки, а тимлид один в белом.

kokoc88

Мне тоже многое не нравится в стиле оформления кода Shurick-а, но это не отменяет необходимость в задачах генерации кода, статической валидации кода и написания такого кода, который удобен для статической валидации.
Только бы не , только бы не ... тьфу. Речь не о том, что есть какая-то необходимость, а о том, что все вокруг не понимают гения! Без статического анализа и ReSharper-а код не может быть читаемый в принципе, и только один разработчик на форуме это понимает!

kokoc88

Возникли проблем? Значит написали мало тестов. Пишите, пишите, обезьянки, а тимлид один в белом.
Вам требуется проверять свой код? Обезьянки! Только один гений пишет идеальный код, статически анализирует его и, не тестируя, сразу же выкладывает в production. За десять лет ни единого разрыва!11

Dasar

ТЕСТЫ!
Интересно, как найти баланс между тестами и статическим анализом для C#-проектов?
Представив этот баланс в виде линии, где слева - много тестов и мало стат. анализа, а справа - много стат. анализа и мало тестов. Подходы, аналогичные твоему, получаются радикально справа - 100% стат. анализа и 0% тестов, подходы скриптовщиков, радикально слева - 0% стат. анализа и 100% тестов.
Вторая шкала: насколько вообще измеряется или гарантируется для проекта, что в нём нет ошибок. При подходах, аналогичных твоему - 20% (условно на php (сайты-визитки) - 0.2%. 100% - это использование и остальных инструментов статической валидации: доказательство валидности, зависимые типы, code analytics и т.д.
Третья шкала: кто делает валидацию ошибок - люди или автоматика? При подходах, аналогичных твоему - 5% люди, 95% автоматика; на php(сайты-визитки) - 100% люди (чаще всего пользователи 0% - автоматика.

6yrop

Подходы, аналогичные твоему, получаются радикально справа - 100% стат. анализа и 0% тестов
Только что переключился из окна Visual Studio, где писал тесты. О каком "моем подходе" ты говоришь?

Dasar

Без статического анализа и ReSharper-а код не может быть читаемый в принципе,
Имхо, без стат. анализа и Resharper-а код неудобен для микро-изменений.
Сейчас по работе много сталкиваюсь с sql-кодом, и вижу что программисты оставляют в нем много соплей по сравнению с C#-кодом. Неудачные названия, неоптимальность конструкций, плохая разбивка на блоки, много copy-paste, раздутые тела и т.д.
В том числе и за собой отмечаю такой подход при написании sql-кода. Связываю это с тем, что уборка этих соплей занимает очень и очень много времени и нервов пока пишешь на sql, без всех этих стат. анализов и решарперов.

Dasar

Только что переключился из окна Visual Studio, где писал тесты. О каком "моем подходе" ты говоришь?
Ок. Был не прав.
По твоему какой у тебя баланс между стат. анализом и тестами?

luna89

Представив этот баланс в виде линии, где слева - много тестов и мало стат. анализа, а справа - много стат. анализа и мало тестов. Подходы, аналогичные твоему, получаются радикально справа - 100% стат. анализа и 0% тестов, подходы скриптовщиков, радикально слева - 0% стат. анализа и 100% тестов.
Это ложная дихотомия, потому что проверка типов дается забесплатно.

Dasar

Это ложная дихотомия, потому что проверка типов дается забесплатно.
Эти дихотомии полезны для измерения разницы между подходами.
Возьмем задачу проверки, что код соответсвует структуре БД и три подхода для обращения к БД (условные Ado.Net, Linq и EF):

DataTable itemTable = new Command("select X, Y from Item where Item.X = {0}", x).Execute;
if (<condition>) y = (int)itemTable.Rows[i]["Y"];


var items = context.Query<Item>("select X, Y, From Item where Item.X = {0}", x);
if (<condition>) y = items[i].Y;


//коллекция для доступа к БД, сгенерированная на основе мета-описания
IQueriable<Item> Items;

var items = context.Items.Where(item => item.X == x).ToArray;
if (<condition>) y = items[i].Y;

В первом случае, стат. анализ используется минимально. Для проверки соответствия кода и БД необходимо много тестов, которые покроют каждый IF.
Соотношение тестов к стат. анализу: 99% к 1%
Во втором случае, появляется использование стат. анализа. Для проверки соответствия кода и БД необходимы тесты по кол-ву Query в коде. Какждый if проверять не требуется.
Соотношение тестов к стат. анализу: 90% к 10%
В третьем случае, стат. анализ используется по максимуму. Необходимо лишь пара тестов для проверки соответствия структуры БД и мета-описания, из которого генерится код для доступа к БД. Соотношение тестов к стат. анализу: 1% к 99%

kokoc88

Для проверки соответствия кода и БД необходимо много тестов, которые покроют каждый IF.
Во всех трёх подходах каждый if уже покрывается за счёт тестов на бизнес логику.

Dasar

Во всех трёх подходах каждый if уже покрывается за счёт тестов на бизнес логику.
Во-первых, останутся If-ы не покрытые бизнес-тестами. Это обычно if-ы, которые срабатывают при ошибке или при нештатной ситуации.
Соответственно, при первом подходе не будет покрыто около 10% if-ов, во втором - 1%, в третьем - 0%.
Во-вторых, бизнес-тесты выполняются от десятков минут до нескольких часов, проверка соответствия кода и БД делается моментально.

kokoc88

Во-первых, останутся If-ы не покрытые бизнес-тестами. Это обычно if-ы, которые срабатывают при ошибке или при нештатной ситуации.
Эти if-ы никак не зависят от проверок типизации.
Во-вторых, бизнес-тесты выполняются от десятков минут до нескольких часов, проверка соответствия кода и БД делается моментально.
Щито?

Dasar

Эти if-ы никак не зависят от проверок типизации.
Это утверждение не соответствует примерам выше.
>> Щито?
Сколько у тебя было на проекте бизнес-тестов? И сколько времени они выполнялись? Сколько в проекте было if-ов?

6yrop

Во всех трёх подходах каждый if уже покрывается за счёт тестов на бизнес логику.
Чем это гарантируется?
Отцу основателю Kent Beck требуется еще 10-20 лет на создание теории: какие тесты надо писать, а какие нет. А Майк уже пользуется следствиями этой теории. Мол, будут обязательно написаны все тесты, которые покрывают sql magic string. Забавно апеллировать к следствиям из несуществующей теории. :grin:

kokoc88

Это утверждение не соответствует примерам выше.
Это утверждение соответствует.
Сколько у тебя было на проекте бизнес-тестов? И сколько времени они выполнялись? Сколько в проекте было if-ов?
Более 10.000 тестов, и выполнялись они за 15-20 минут, но это никак не влияет на мою сомневаку.

kokoc88

Отцу основателю Kent Beck требуется еще 10-20 лет на создание теории: какие тесты надо писать, а какие нет. А Майк уже пользуется следствиями этой теории. Мол, будут обязательно написаны все тесты, которые покрывают sql magic string. Забавно апеллировать к следствиям из несуществующей теории.
Детонька, ты продолжай, продолжай думать, что важно только совпадение типов. Остальное таким гениям, как ты, не нужно. А все, кто смеётся над твоими гениальными идеями, кому нужны тесты, кому нужно ООП - они плохие глупые обезьянки.

6yrop

Возьмем задачу проверки, что код соответсвует структуре БД и три подхода для обращения к БД
Ты задаешь эти вопросы человеку, который использует RDBMS как key-value storage. И употребляет словосочетание "реляционные отношения". Только одно это словосочетание говорит о том, что человек не знаком с основами реляционной теории. Человек не умеет и не хочет работать с реляционкой, а ты ему про высокие материи: валидацию кода и т.п.

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

kokoc88

Ты задаешь эти вопросы человеку, который использует RDBMS как key-value storage. И употребляет словосочетание "реляционные отношения". Только одно это словосочетание говорит о том, что человек не знаком с основами реляционной теории. Человек не умеет и не хочет работать с реляционкой, а ты ему про высокие материи: валидацию кода и т.п.
Мы разговариваем с человеком, у которого не всё в порядке с головой. Он всё время жалуется на тех, кто умнее его. Только одно это говорит о том, что человек не готов к признанию реальности и вопреки всему считает, что он адекватен. На деле же его ранимая детская психика не готова к серьёзным разговорам, а мы ему про высокие материи: тестирование, читаемость кода, и т.п.
Пользователь форумлокал сказал мне сделать суицид

6yrop

Мы разговариваем с человеком, у которого не всё в порядке с головой. Он всё время жалуется на тех, кто умнее его. Только одно это говорит о том, что человек не готов к признанию реальности и вопреки всему считает, что он адекватен. На деле же его ранимая детская психика не готова к серьёзным разговорам, а мы ему про высокие материи: тестирование, читаемость кода, и т.п.
Серьёзные разговоры с умными людьми, которые предлагают собеседнику суицид. Абсурд же! :grin:

kokoc88

Серьёзные разговоры с умными людьми, которые предлагают собеседнику суицид. Абсурд же!
Да-да, серьёзные разговоры с человеком, который проиграл на Форексе, и испытал такой баттхёрт, что написал о применении ядерного оружия. Первый и уникальный случай ядерного баттхёрта на форумлокал.

stm5872449

И употребляет словосочетание "реляционные отношения".
И что не так с этим словосочетанием?

6yrop

Умный человек, ты не понял моего поста про ядерное оружие.

6yrop

И что не так с этим словосочетанием?
Попробуй перевести его на английский.

kokoc88

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

kokoc88

Попробуй перевести его на английский.
Только "фрайворк" не переводи. Хотя бы не перед сном.

6yrop

Только "фрайворк" не переводи. Хотя бы не перед сном.
"фрайворк" по английски framework. Что тебя смущает?

6yrop

Петушочек, ты пойди мне в той теме возрази.
Ты предлагаешь Петушку возражать Умному Человеку?

YUAL

"фрайворк" по английски framework. Что тебя смущает?
framework [ˈfreɪmwɜːk] - фрэймвок
А фрайворк это скорее frywork (жарительная работа?)

6yrop

По твоему какой у тебя баланс между стат. анализом и тестами?
If I don't typically make a kind of mistake ..., I don't test for it. (с) Kent Beck

6yrop

За десять лет ни единого разрыва!11
На твоих проектах (с тестами) ни единого разрыва?

6yrop

Это ложная дихотомия, потому что проверка типов дается забесплатно.
Давай погорим о твоем любимом GraphQL.
Тут говорится, что "A GraphQL query is a string interpreted by a server". Т.е. это аналогично sql magic string внутри кода на языке общего назначения (C#/Java и т.д.)? Тогда возникает вопрос, как статическим анализом кода проверять запросы, которые собираются посредством конкатенации строк?

kokoc88

Ты предлагаешь Петушку возражать Умному Человеку?
Я предлагаю тебе захлопнуть свой клювик.

kokoc88

"фрайворк" по английски framework. Что тебя смущает?
Меня ничего не смущает. Но только одно это заявление говорит о том, что ты вообще ничего не знаешь об основах разработки программного обеспечения.

6yrop

ты пойди мне в той теме возрази.

Я предлагаю тебе захлопнуть свой клювик.
Душевные метания гопника Умного Человека.

kokoc88

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

luna89

Тебе пришлось вынести редактируемое состояние контрола в state доменной модели, потому что типы данных доменной модели и модели контрола различаются.
На днях писал формы на реакте, вспомнил этот тред. Загрязнять доменную модель строками не годится, тут согласен. Допилил свой пример с калькулятором. Там теперь есть возможность делать из текстового инпута и пары функций парсер/форматтер новый инпут, который сразу работает с отпарсенными данными (у меня там intInput, который принимает и возвращает Maybe Int).
Затем, я придумал прикольную функцию combineObject. Она проходится по всем свойствам объекта (в нашем случае {first: Maybe Int, second: Maybe Int} ищет там Nothing, и если все ок то возвращает Just {first: Int, second: Int}, иначе Nothing.
Таким образом, если у нас форма о 20 полях, то чтобы проверить что она заполнена, не надо писать багоопасный код сложения 20 флагов. У нас просто каждый виджет будет смотреть в определенное поле объекта и читать/писать туда Maybe<T>. Потом можно сделать combineObject и посмотреть, что получилось - Just или Nothing. На статическом языке написать такую функцию не получится, ну или потребуется очень сильное метапрограммирование.
Затем, мы можем просто писать функции, которые работают с объектом формы, но уже без Maybe, и применять эти функции через fmapObject. Таким образом, удается избежать заражения кода монадой. Джаваскрипт позволяет написать сразу целый модуль таких функций, и сразу все одной строчкой обернуть во fmapObject.

Dasar

На статическом языке написать такую функцию не получится, ну или потребуется очень сильное метапрограммирование.
Потребуется одна функция - которая отображает поля класса на dictionary. В JS - эта функция из коробки (в нём каждый object изначально является dictionary а в static языках она возьмется из custom library.

6yrop

Затем, я придумал прикольную функцию combineObject. Она проходится по всем свойствам объекта (в нашем случае {first: Maybe Int, second: Maybe Int} ищет там Nothing, и если все ок то возвращает Just {first: Int, second: Int}, иначе Nothing.
Таким образом, если у нас форма о 20 полях, то чтобы проверить что она заполнена, не надо писать багоопасный код сложения 20 флагов. У нас просто каждый виджет будет смотреть в определенное поле объекта и читать/писать туда Maybe<T>. Потом можно сделать combineObject и посмотреть, что получилось - Just или Nothing. На статическом языке написать такую функцию не получится, ну или потребуется очень сильное метапрограммирование.
Забавно, что для объяснения условия ты используешь статически типизированную нотацию. :grin: А говорил, что читаемость это конек динамических языков. ;)

6yrop

Потребуется одна функция - которая отображает поля класса на dictionary. В JS - эта функция из коробки (в нём каждый object изначально является dictionary а в static языках она возьмется из custom library.
говорит про то, что в C#/Java можно написать вот такую функцию для Tuple

Option<Tuple<T1, T2>> CombineTuple<T1>(Tuple<Option<T1>, Option<T2>> tuple)
{
...
}

Но нельзя аналогичную функцию написать для произвольного объекта.
Да, было бы здорово иметь named tuple (как в Python) в C#.
Возвращать dictionary не круто, поскольку теряется статическая типизация.

6yrop

Потом можно сделать combineObject и посмотреть, что получилось - Just или Nothing. На статическом языке написать такую функцию не получится, ну или потребуется очень сильное метапрограммирование.
А еще есть кодогенерация. :grin:

6yrop

На днях писал формы на реакте, вспомнил этот тред. Загрязнять доменную модель строками не годится, тут согласен. Допилил свой пример с калькулятором. Там теперь есть возможность делать из текстового инпута и пары функций парсер/форматтер новый инпут, который сразу работает с отпарсенными данными (у меня там intInput, который принимает и возвращает Maybe Int).
Затем, я придумал прикольную функцию combineObject. Она проходится по всем свойствам объекта (в нашем случае {first: Maybe Int, second: Maybe Int} ищет там Nothing, и если все ок то возвращает Just {first: Int, second: Int}, иначе Nothing.
Таким образом, если у нас форма о 20 полях, то чтобы проверить что она заполнена, не надо писать багоопасный код сложения 20 флагов. У нас просто каждый виджет будет смотреть в определенное поле объекта и читать/писать туда Maybe<T>. Потом можно сделать combineObject и посмотреть, что получилось - Just или Nothing. На статическом языке написать такую функцию не получится, ну или потребуется очень сильное метапрограммирование.
Затем, мы можем просто писать функции, которые работают с объектом формы, но уже без Maybe, и применять эти функции через fmapObject. Таким образом, удается избежать заражения кода монадой. Джаваскрипт позволяет написать сразу целый модуль таких функций, и сразу все одной строчкой обернуть во fmapObject.
Давайте посмотрим на subject треда, там идет речь про поля (не про объекты а ты опять вводишь функцию с названием combineObject.
Если у тебя есть механизм из первого поста:

легко добавлять, удалять, менять обработку поля? Поле должно легко протаскиваться и искаться по всем слоям от БД до пользовательского интерфейса.

то формулировать задачи в терминах объектов тебе не нужно, просто оперируешь терминами на уровне полей. Терминология на уровне полей естественна, и заказчики именно этой терминологией пользуются. Поэтому когда код наиболее приближен к терминологии заказчиков жизнь становится проще и все становятся довольными. :) Делайте мир лучше. А не закапывайтесь в програмерские объекты.

kokoc88

На статическом языке написать такую функцию не получится, ну или потребуется очень сильное метапрограммирование.
В этом разделе ты повторил эту фразу уже несколько десятков раз. И каждый раз она не соответствовала действительности. Зачем ты всё время пишешь о том, чего не знаешь? Сильное метапрограммирование потребуется только на C++, на Java или C# мы легко напишем фреймворк, который обработает атрибуты вида [Field(true)] int second; и нам не придётся писать ничего, кроме одного атрибута на поле.
Чуть дальше в реальность. Функция combineObject на самом деле только первый шаг к чему-то приличному. В реальном проекте нам потребуется выводить сообщения о том, какое именно поле не заполнено или заполнено неправильно. Устанавливать фокус ввода на первое неправильное поле. Закрашивать его другим цветом. И всё это на C# делается одним атрибутом на поле и одной строчкой для привязки к представлению поля. Правда, если подумать ещё немного, то окажется, что обязательность и валидация одних полей зависят от значений в других полях, и код сложной валидации придётся-таки написать руками. А ещё сообщения от валидации могут зависеть от назначения поля и должны поддерживать интернационализацию.
Собственно, со всякими модными новыми языками и технологиями, которых наплодили уже over 9000, всегда так. Кто-то под эмоциями от какой-то платформы садится, рисует простой туториал, не имеющий никакого отношения к реальности, выдаёт это за вселенское достижение и делает на этом стартап. А потом приходят реальные требования, и этот красивый маленький кусочек кода вырастает в монстра, которого потом выкинут на помойку, если стартап раньше не развалится.

6yrop

Кто-то под эмоциями от какой-то платформы садится, рисует простой туториал, не имеющий никакого отношения к реальности
А к каким технологиям или патернам есть примеры, которые имею отношения к реальности? Вот Фаулер в своих книгах честно признается:

I like to use examples that are no less realistic as you usually find in books like this.
http://martinfowler.com/eaaDev/uiArchs.html

6yrop

вики, msdn, Fowler
Перечитываю тут Фаулера. У него изначально вводится неверный постулат про три копии данных:

С точки зрения пользователя есть всего две копии данных. Первая копия — это общее долговременное хранилище (там постоянно хранятся данные от всех пользователей). И кратковременная копия, с которой работает пользователь. Всё! Только две копии. Дублирование данных в "session state" и "screen state" — это не является свойством изначальной задачи, не является essential complexity. Такое дублирование — это чистой воды accidental complexity.

Dasar

Auth-cookie где находится? в Screen-state? или всё-таки в session-state?
Undo/Redo данные где лежат?

6yrop

Auth-cookie где находится? в Screen-state? или всё-таки в session-state?
Undo/Redo данные где лежат?
Проблема в самих названиях "session state" и "screen state". Главное не должно быть дублирования. Должен быть один экземпляр mutable данных. А название можно придумать, например, user state.

luna89

В этом разделе ты повторил эту фразу уже несколько десятков раз. И каждый раз она не соответствовала действительности. Зачем ты всё время пишешь о том, чего не знаешь? Сильное метапрограммирование потребуется только на C++, на Java или C# мы легко напишем фреймворк, который обработает атрибуты вида [Field(true)] int second; и нам не придётся писать ничего, кроме одного атрибута на поле.
В java/c# нельзя выразить тип функции combineObject. Использование такой функции будет динамическим, так же как в js. Кроме того, тебе потребуется руками написать оба типа - первый который приходит на вход функции combineObject, второй который она возвращает. Вроде бы в языке D можно в compile time сгенерить второй тип из первого, так чтобы абсолютно все было типизировано, но я не уверен.
Что касается атрибутов, то это типичное джава-убожество. Например, если у меня есть атрибуты Foo и Bar, то я не могу сделать атрибут FooBar, который работал бы как их комбинация. Кроме того, я не могу написать функцию, которая возвращает атрибут. В общем, если сущности не композируются и не взаимодействуют с другими сущностями, то это не программирование.
И всё это на C# делается одним атрибутом на поле и одной строчкой для привязки к представлению поля.
А как называется этот C# фреймворк? Вот тут Шурик показывал какой-то - без слез не взглянешь.

kokoc88

В java/c# нельзя выразить тип функции combineObject.
В решении с reflection это не нужно.
Что касается атрибутов, то это типичное джава-убожество. Например, если у меня есть атрибуты Foo и Bar, то я не могу сделать атрибут FooBar, который работал бы как их комбинация. Кроме того, я не могу написать функцию, которая возвращает атрибут. В общем, если сущности не композируются и не взаимодействуют с другими сущностями, то это не программирование.
Куча воды, а по существу ничего не возразил. Если тебе не хочется, то можешь не называть упомянутые мною две строчки кода программированием, но задачу они решают полностью. Чтобы спорить о недостатках Java, тебе нужно сначала чему-то научиться. А так ты только ещё раз продемонстрировал, что у тебя от этого языка программирования какой-то нереальный батхёрт.
А как называется этот C# фреймворк? Вот тут Шурик показывал какой-то - без слез не взглянешь.
Никак не называется, пишется за день или два с поддержкой всех требований, которые я перечислил. Выглядеть будет так:
[Field(IsRequired = true)] int first;
view.Add(model => model.first);

luna89

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

luna89

В решении с reflection это не нужно.
Да, но это динамическая типизация, так как у твоей функции combineObject будет тип Object => Object. Я как раз и написал, что сделать такую функцию типизированной можно на очень ограниченном количестве языков.

kokoc88

Ты полностью решил задачу двумя воображаемыми строчками кода на несуществующем фреймворке?
Я полностью решил эту задачу в реальном проекте, написав такой фреймворк. Ты ведь понятия не имеешь, как это можно сделать, да? Поэтому и выглядишь глупо.
Да, но это динамическая типизация, так как у твоей функции combineObject будет тип Object => Object. Я как раз и написал, что сделать такую функцию типизированной можно на очень ограниченном количестве языков.
Во-первых, мне как-то наплевать на такую функцию, она не решает задачи, которые возникнут в реальном проекте. Во-вторых, я не молюсь богам статической типизации, ты меня с кем-то спутал.

6yrop

Я полностью решил эту задачу в реальном проекте, написав такой фреймворк.
А делает нереальные проекты?

6yrop

не надо писать багоопасный код сложения 20 флагов.
Просто пишешь тесты и багов нет.

kokoc88

А делает нереальные проекты?
Я без понятия, что он делает. А вот твои проекты и правда далеко от реальности.

6yrop

А вот твои проекты и правда далеко от реальности.
Я вчера покверил продакшен базу своего последнего проекта, на предмет статистики. Остался доволен. :D Таким образом, ты врешь. Может ты и про свои "реальные" проекты врешь? ;)

kokoc88

Таким образом, ты врешь. Может ты и про свои "реальные" проекты врешь?
Может и вру. Но настоящее, кристальное чистое враньё в Development - это твои воображаемые коллеги, которым нравится ко-ку. Ты выдумал их, потому что очень сильно переживаешь из-за того, что происходит на самом деле - этот проект говно и никому не нужен.

6yrop

Действительно, не нужен для тех, на кого просветление, что RDBMS проще использовать как key-value storage.
Остальным (которых просветление еще не посетило) скармливают Entity Framwork, который в седьмой версии полностью переписывают.
А в настоящей реальности работает 1C.

kokoc88

Действительно, не нужен для тех, на кого снизошло просветление, что RDBMS проще использовать как key-value storage.
Ты не совсем понял смысл моего поста. Ко-ку не просто кому-то не нужен - он полное говно.

luna89

Во-первых, мне как-то наплевать на такую функцию, она не решает задачи, которые возникнут в реальном проекте.
Как ты моделируешь отсутствующие данные?

kokoc88

Как ты моделируешь отсутствующие данные?
Какие? Я вроде бы писал выше. Если представление с состоянием и поле по требованиям обязательное, то мне не нужно ничего моделировать. Если поле по требованиям необязательное, то простой nullable тип.

6yrop

то простой nullable тип.
Nullable типы в C# это maybe монада с ограничением только на value-типы.

kokoc88

Nullable типы в C# это maybe монада с ограничением только на value-типы.
Какой ужас, как я теперь буду жить?

6yrop

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

luna89

Если поле по требованиям необязательное, то простой nullable тип.
Ты не пытаешься абстрагировать проверки на null? Чтобы функция, которая что-то считает, была избавлена от необходимости проверять на null, а проверки были отдельно?

kokoc88

Ты не пытаешься абстрагировать проверки на null? Чтобы функция, которая что-то считает, была избавлена от необходимости проверять на null, а проверки были отдельно?
В интерфейсе это не нужно. Там всё это покрывается тем самым загадочным фреймворком, про который я то ли наврал, то ли нет. Функции, которые что-то считают, обычно не получают null. Ничего хорошего при натягивании арифметики на null обычно не получается.

luna89

В интерфейсе это не нужно.
Вот как раз в калькуляторе не надо считать сумму если данные невалидные.

6yrop

Ты не совсем понял смысл моего поста. Ко-ку не просто кому-то не нужен - он полное говно.
Ты о моем проекте думаешь больше, чем я сам. Я готов согласиться, что Controllable Query полное говно. Но что мне использовать в следующем проекте? Entity Framework? Какой версии? Седьмая еще не вышла, шестая уже устарела. Или использовать RDBMS как key-value storage? Или каждый запрос оформлять в отдельный метод и писать на него тест, в котором наполнять базу данных и проверять правильно ли прочиталось из базы каждое поле?

kokoc88

Вот как раз в калькуляторе не надо считать сумму если данные невалидные.
Но для этого не нужно никаких nullable полей.

luna89

Ты о моем проекте думаешь больше, чем я сам. Я готов согласиться, что Controllable Query полное говно. Но что мне использовать в следующем проекте? Entity Framework? Какой версии? Седьмая еще не вышла, шестая уже устарела. Или использовать RDBMS как key-value storage? Или каждый запрос оформлять в отдельный метод и писать на него тест, в котором наполнять базу данных и проверять правильно ли прочиталось из базы каждое поле?
Организуй доступ к данным через слой вьюх, вьюхи типизированные.

6yrop

Вот пример из реального проекта http://samsaffron.com/archive/2011/09/05/Digging+ourselves+o...
Как такое делать на вьюхах? Приведи код, как бы ты это сделал?

luna89

Как такое делать на вьюхах? Приведи код, как бы ты это сделал?
Там много переделать можно. Например, надо сделать вьюху can_vote_for_tag(tag_id, can_vote(boolean. Надо приджойнивать эту вьюху всегда, независимо от условий. Дальше если столбцы из нее не используются, то оптимизатор удалит ненужные джоины. В общем, ты все параметры по которым собираешься фильтровать или сортировать выводишь во вьюху, а потом в самом конце генеришь sql типа "select col1,col2,... from superview where some_filter_col = ? order by some_sort_col". Динамическая генерация минимальна.
Тесты тут обязательны конечно. Нужна стандартная фикстура с несколькими пользователями, вопросами, тегами etc. Относительно этой фикстуры пишутся все тесты. Тут тестировать немного, при том что это мажорная фича stackoverflow. Люди вон тестируют UI виджеты в клауде под 20 разных браузеров, скриптами эмулируя движения мышкой. А тут чистая функция от базы и параметров, детский сад.

luna89

Или каждый запрос оформлять в отдельный метод и писать на него тест, в котором наполнять базу данных и проверять правильно ли прочиталось из базы каждое поле?
Мне даже интересно стало, а как ты по-другому собрался делать? Ты будешь руками накликивать, создавая пользователей, тэги, накручивать лайки и звездочки, а потом пристальным взглядом смотреть, что все правильно выводится? А что если UI делает отдельный человек уже после того как ты предоставил API?

6yrop

Дальше если столбцы из нее не используются, то оптимизатор удалит ненужные джоины. В общем, ты все параметры по которым собираешься фильтровать или сортировать выводишь во вьюху, а потом в самом конце генеришь sql типа "select col1,col2,... from superview where some_filter_col = ? order by some_sort_col".
Т.е. по человечески SQL запросы писать нельзя.
Спасибо, но в продакшен я с таким не пойду.
Тесты тут обязательны конечно. Нужна стандартная фикстура с несколькими пользователями, вопросами, тегами etc. Относительно этой фикстуры пишутся все тесты.

Никто не спорит, что тесты будут. Вопрос сколько и какие тесты писать?
С одной стороны, со слов Kent Beck на сегодняшний день не существует универсальной теории какие тесты писать, а какие не писать.
С другой стороны, у нас есть конкретный пример. Покажи, как ты бы оформил код, чтобы его можно было тестировать? И какие бы тесты ты бы написал?

luna89

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

6yrop

Как ты оформишь код с запросами?
Сколько и какие тестовые данные подготовишь?
С какими параметрами будешь вызывать запрос?
Сколько строчек/символов кода у тебя это займет?
Как долго ты это будешь отлаживать?
Какое количество ошибок сделаешь в тестах?
Сколько времени потратишь на всё это?

6yrop

Интересно обсудить, какие бы ты использовал подходы, и какой бы ты рекомендовал стиль код для решения задач:
- генерация dbml из БД
- генерация кода на основе dbml
- исполнение EF-запросов
- проверки того, что в этих генерациях минимизированы ошибки
Тимлиды бывают двух видов. У одних есть хороший, проверенный временем стиль, который они рекомендуют другим разработчиками, приводя обоснования. Другие умение работать с баранами.
Проблему взаимодействия C#/Java/JS с базой осознают оба ведущих поставщика RDBMS: Oracle и Microsoft.
Слайд с сайта Oracle:

Oracle видит решение в том, чтобы запихнуть весь SQL в объекты базы.
Microsoft встроила в C# язык запросов.
Я готов сравнивать эти подходы с Controllable Query, но не готов сделать суицид, как рекомендует прокаченный в работе с баранами тимлид.

kokoc88

Я готов сравнивать эти подходы с Controllable Query, но не готов сделать суицид, как рекомендует прокаченный в работе с баранами тимлид.
Потратить два часа на пост, смысл которого в том, чтобы просто послать меня на хуй? Интересно, сколько стульев сгорело, пока ты его писал.
Что поделать, программистишко из тебя так себе. Может быть, тебе стоит поискать смысл жизни в чём-то другом? Например, пойти работать в макдак, уровня твоего интеллекта как раз хватит. Всего лишь нужно научиться вместо "Controllable Query" всё время кричать "Свободная касса".

6yrop

Ты используешь RDBMS как key value storage, пишешь говно фрейворки на атрибутах. Честно говоря, я упорно до последнего думал, что ты что-то из себя представляешь как программист. Оказывает всё гораздо хуже, ты реализовываешь наихудшие практики.

kokoc88

Ты используешь RDBMS как key value storage, пишешь говно фрейворки на атрибутах. Честно говоря, я упорно до последнего думал, что ты что-то из себя представляешь как программист. Оказывает всё гораздо хуже, ты реализовываешь наихудшие практики.
Напиши, какой я плохой, в СпортЛото. Там тебя поймут, я гарантирую это.

6yrop

Ты уже писал?

kokoc88

Ты уже писал?
Нет, мне просто нравится творчество Высоцкого.

6yrop

Видимо, до тебя и в его творчестве не всё доходит. Иначе сложно объяснить твою гаденькую душонку.

kokoc88

Видимо, до тебя и в его творчестве не всё доходит. Иначе сложно объяснить твою гаденькую душонку.
Я понимаю, что ты долго и упорно писал ко-ку, а в итоге получилось никому не нужное говно. Но нельзя же судить людей только за то, что они сказали тебе правду. Хотя вряд ли эти слова до тебя дойдут, ведь в твоём понимании "форум плохой", а "Шурик хороший".

6yrop

Как только мне скажут как работать из C# с SQL Server, я тут же откажусь от Controllable Query.

luna89

Напомни кстати, что делает Controllable-Query? Я общий смысл понял, но детали не уловил.
Насколько я понял, ты где-то пишешь чистые функции (boolean, boolean, ...) => SQLQuery, как-то аннотируешь их, и во время билда генерятся все пермутации аргументов и выпоняются запросы?

6yrop

Controllable Query выполняет тестирование запросов.
Из C#/Java работа с RDMS идет через ADO.NET/JDBC. Формируется строка SQL запроса и коллекция параметров — в ADO.NEТ для этих целей используется SqlCommand, в JDBC — PreparedStatement. Запрос отправляется в СУБД, и мы получаем DataReader (в JDBC это ResultSet). Поля DataReader обычно мепят на свойства некоторого типа T001 — названия и типы свойств T001 совпадают с названиями и типами полей в DataReader. В коде это выглядит так:

static IEnumerable<T001> XxxQuery(DateTime? startDate, DateTime? endDate)
{
var command = new SqlCommand;
var builder = new StringBuilder(@"
SELECT Id, CreateDate
FROM Post
WHERE 1 = 1");
if (startDate.HasValue)
{
builder.Append(@"
AND CreateDate >= @startDate");
command.Parameters.AddWithValue("@startDate", startDate.Value);
}
if (endDate.HasValue)
{
builder.Append(@"
AND CreateDate <= @endDate");
command.Parameters.AddWithValue("@endDate", endDate.Value);
}
command.CommandText = builder.ToString;
return command.GetEnumerable<T001>
}

interface T001
{
int Id {get;}
DateTime? CreateDate {get;}
}

Controllable Query делает следующее. Находит все методы, где вызывается сетер свойства command.CommandText. Тестирует найденные методы. Смотрит на типы параметров метода. Для типов можно задать наборы тестовых значений. Например, для "DateTime?" — null и new DateTime(2001, 1, 1); для string — null и "test string". Если встретился тип, для которого не задан набор тестовых значений, то ругаемся. CQ вызывает тестируемый метод, получает sql-строку, набор sql-параметров и тип результата T001. Затем он оправляет sql-строку и sql-параметры в СУБД в режиме schema only. В этом режиме СУБД проверяет валидность запроса и возвращает названия и типы полей, которые есть в DataReader. CQ проверяет, совпадают ли названия и типы полей в DataReader с названиями и типами свойств T001.
Еще CQ может отдать sql-строку и sql-параметры в СУБД и спросить у СУБД, используется ли в этом запросе заданная таблица, колонка, вьюшка и т.д. CQ показывает имя файла и номер строки, где начинается c#-метод, который сформировал данную sql-строку.

6yrop

Мимо тебя совершенно точно проходит, что ещё больше говорят о повышении читаемости кода, автоматическом тестировании и сокращении boilerplate. Кстати, ты всё время сливаешься в спорах про языки с динамической типизацией, т.к. скудоумие не даёт тебе понять, что для некоторых групп разработчиков читаемость более приоритетна, чем все плюсы статической типизации вместе взятые.
Вот определение читаемости кода из Википедии:
In computer programming, readability refers to the ease with which a human reader can comprehend the purpose, control flow, and operation of source code. It affects the aspects of quality above, including portability, usability and most importantly maintainability.
Readability is important because programmers spend the majority of their time reading, trying to understand and modifying existing source code, rather than writing new source code.
http://en.wikipedia.org/wiki/Computer_programming#Readabili...

Именно maintainability посвящено большинство моих постов в этом разделе.
В больших коммерческих проектах очень важную роль начинают играть средства поиска, навигации по коду, возможность безболезненно поменять код и т.д. Уменьшение mutable состояния очень сильно способствует всему перечисленному.
Ты, видимо, под читаемостью подразумеваешь что-то своё личное, какие-то особенности твоего вкуса.

kokoc88

Ты, видимо, под читаемостью подразумеваешь что-то своё личное, какие-то особенности твоего вкуса.
Запомни эту фразу, подойди к зеркалу и произнеси её 1024 раза.

6yrop

Запомни эту фразу, подойди к зеркалу и произнеси её 1024 раза.
Мой вкус относительно языков совпадает с командой Хельсберга, которая делает C# и TypeScript.
Вкус относительно работы с СУБД совпадет с теми, которые не любят ORM, не любят expression trees. Это отражено в так называемых микро-орм.

kokoc88

Мой вкус относительно языков совпадает с командой Хельсберга, которая делает C# и TypeScript.
Да что ты говоришь! И какие нотки ты различаешь в языках? Корицу и фрукты?

luna89

Мой вкус относительно языков совпадает с командой Хельсберга, которая делает ... TypeScript.
Боюсь, это вкус говнеца - геттеры, сеттеры, аннотации.

6yrop

Боюсь, это вкус говнеца - геттеры, сеттеры, аннотации.
Расскажи про свой вкус. :grin: Например, GraphQL — читаем Why invent something new? — описывается проблемы, которых при удаленном взаимодействии C#<->C# просто нет. :)
 
... grow into a maintenance nightmare ..

Необразованные дети. Статическая типизация является отличным средством для maintenance.

luna89

Например, GraphQL — читаем Why invent something new? — описывается проблемы, которых при удаленном взаимодействии C#<->C# просто нет.
Ну тупи, GraphQL - это кроссплатформенный формат, разработан чтобы сервер на каком-нибудь PHP обменивался данными с js, java, objective-c. Обмениваться данными C#<->C# никому не нужно.

6yrop

кроссплатформенный формат, разработан чтобы сервер на каком-нибудь PHP обменивался данными с js, java, objective-c.
Архитекторы астронавты разрабатывают "кросплатформенные" форматы каждые два года. Мне уже скучно.
Заказчику нужно работающее приложение. А кросплатформенность это забота программиста, выбирающего зоопарк технологий. :grin:
И, да, моему работодателю нужен C#<->C#.

luna89

Уменьшение mutable состояния очень сильно способствует всему перечисленному.
Имхо, сейчас есть единственный простой язык с немутабельными структурами данных - clojure, и он динамический. Там есть удобные функции, например assoc-in.
На статических языках ничего похожего по простоте и удобству нет.

6yrop

Имхо, сейчас есть единственный простой язык с немутабельными структурами данных - clojure, и он динамический. Там есть удобные функции, например assoc-in.На статических языках ничего похожего по простоте и удобству нет.
Зачем впадать в академические крайности? Если для мутабельности есть причины, то мутабельность надо использовать. Для сложения двух чисел, взятых из текстбоксов, такой причины нет. Для энтерпрайза статический C# и мутабельность вполне себе сочетаются. Кстати, Roslyn запилили тоже во много имутабельным.

luna89

Если для мутабельности есть причины, то мутабельность надо использовать.
На C# тебе придется использовать мутабельность, даже если причин для этого нет. Ты банально не сможешь без бойлерплейта сделать такое:
(def users [{:name "James" :age 26} {:name "John" :age 43}])
;; update the age of the second (index 1) user
(assoc-in users [1 :age] 44)
;;=> [{:name "James", :age 26} {:name "John", :age 44}]

luna89

Зачем впадать в академические крайности?
Когда-то структурное программирование считалось академической крайностью, потом сборка мусора, потом первоклассные функции. То, что с точки зрения более низкоуровнего языка является крайностью, для более высокоуровневого - естественный идиоматичный код.

Dasar

Ты банально не сможешь без бойлерплейта сделать такое:
без статической проверки:

users = users.AssocIn(1, "age", 44);

со статическими проверками

users = users.AssocIn(1, _=>_.age, 44);

luna89

Какой тип у функции AssocIn?

Dasar


IEnumerable<T> AssocIn<T, TValue>(this IEnumerable<T> items, int index, Expression<Func<T, TValue>> f, TValue v);

luna89

assoc-in работает на структуры данных любой глубины, там может быть любая последовательность массивов и ассоциативных массивов.

Dasar


users = users.Update(_=>_.By(1).Cars.By(0).Wheels.By(3).Disk.Color, Color.Red);

IEnumerable<T> Update<T, TValue>(this IEnumerable<T> items, Expression<Func<T, TValue>> f, TValue v);

luna89

Ты уверен, что понимаешь, как работает assoc-in? Он на каждом уровне клонирует старую структуру, заменяя одно поле (на самом деле используются персистентные структуры данных).
Ты пишешь на псевдокоде или на C#? На C# ты такого не сделаешь без зашкаливающего количества магии, которая сделает идею непрактичной. Если на псевдокоде, то я согласен, что можно реализовать такой статически типизированный язык, но пока никто не сделал.

Dasar

Ты уверен, что понимаешь, как работает assoc-in?
Да, понимаю.
Ты пишешь на псевдокоде или на C#?
на C#.
Update с expression-ом - это сахар для:

users = users.Update(1, user => user.With(cars:user.Cars
.Update(0, car => car.With(wheels:car.Wheels.Update(3, wheel => wheel.With(disk:wheel.Disk.With(color:Color.Red;

6yrop

... причин для этого нет. ...
...
 update ...
 
Как это нет причин, слово update является прямой причиной, указанной в условии задачи.
Учитесь читать то, что вас просят сделать. И не занимайтесь отсебятиной.

luna89

Update с expression-ом - это сахар для
Откуда берутся функции users.Update и user.With?

Dasar

Откуда берутся функции users.Update и user.With?
Update - это функция immutableCollection, With - это функция, которая генерится для каждого immutable-класса.

luna89

которая генерится для каждого immutable-класса.
Кем генерится?

Dasar

Кем генерится?
codegenerator-ом. Который перед build-ом пробегает исходники, парсит их с помощью Roslyn-а, выдергивает immutable-классы и для них генерит сахар-ные функции

luna89

codegenerator-ом. Который перед build-ом пробегает исходники, парсит их с помощью Roslyn-а, выдергивает immutable-классы и для них генерит сахар-ные функции
Да, если постараться, то кодогенерацией можно навелосипедить, но на практике это неюзабельно.

Dasar

но на практике это неюзабельно.
почему?

6yrop

на практике это неюзабельно.
Ты ищешь практическую юзабельность в академических изысканиях?

luna89

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

Dasar

Знаешь какие-нибудь примеры, чтобы такого рода кодогенерация была популярна?
WindowsForms, EntityFramework и Asp.net построены на кодогенерации

6yrop

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

EntityFramework

Говно же.

Asp.net

Ты про какую кодогенерацию? Про ту что в WebForms? WebForms это говнищееееее!
Razor это няшная кодогенерация, которая по хорошему должна стать частью языка C#.
Сейчас ASP MVC 6 сделали еще что-то, опять тащат какое то говно в разметку, вместо чистого Razor.

Dasar

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

stm5872449

Ты ищешь практическую юзабельность в академических изысканиях?
Персистентные структуры данных - это академические изыскания? Лол.

6yrop

Персистентные структуры данных - это академические изыскания? Лол.
В рамках темы этого треда, да.

6yrop

Боюсь, это вкус говнеца - геттеры, сеттеры, аннотации.
Внезапно, Angular2 написали на TypeScript:

luna89

Внезапно, Angular2 написали на TypeScript:
Да, подобное тянется к подобному:

@Component({
selector: 'talk-list',
viewInjector: [TalksAppBackend]
})
class TalkList {
constructor(backend:TalksAppBackend) {
this.talks = backend.fetchTalks;
}
}

Обычная ситуация в кафкианском мире джава-программирования - я не могу сам передать аргумент в функцию, мне надо просить об этом какой-то Фреймворк™ с помощью Аннотаций™. Сам Фреймворк™ передаст аргумент не просто так, а с помощью Инджектора™, причем не просто Инджектора™, а ВьюИнджектора™ (думаю, там есть иерархия Инджекторов™, унаследованных от какого-нибудь БазовогоИнджектора™).
К счастью, сейчас не 2005 год, и авторы и пользователи этого творчества вызывают даже уже не смех, а откровенную жалость.

luna89

Внезапно, Angular2 написали на TypeScript:
Кстати, это не совсем так. Вроде бы код ангуляра одновременно является кодом на typescript и на Dart, вроде бы в ci он компилится сразу двумя компиляторами.

6yrop

и на Dart
Дарт это гугл, а гугл языки писать не умеет.

6yrop

Обычная ситуация в кафкианском мире джава-программирования - я не могу сам передать аргумент в функцию, мне надо просить об этом какой-то Фреймворк™ с помощью Аннотаций™. Сам Фреймворк™ передаст аргумент не просто так, а с помощью Инджектора™, причем не просто Инджектора™, а ВьюИнджектора™ (думаю, там есть иерархия Инджекторов™, унаследованных от какого-нибудь БазовогоИнджектора™).
К счастью, сейчас не 2005 год, и авторы и пользователи этого творчества вызывают даже уже не смех, а откровенную жалость.
Я тут нашел классный фрейворк:
Vanilla JS is a fast, lightweight, cross-platform framework
for building incredible, powerful JavaScript applications.
http://vanilla-js.com/

Идеально сочетается с TS.

zya369

vanilla-C# еще зацени, поймешь, что твой CQ нафиг не нужен

6yrop

vanilla-C# еще зацени, поймешь, что твой CQ нафиг не нужен
Отлично! Ты захватил наживку. Я хочу торгануть CQ как ванильный ADO.NET. Поэтому и начал гуглить слово vanilla.
Найди отличие от ванильного C#:
 
 
static IEnumerable<T001> XxxQuery(DateTime? startDate, DateTime? endDate)
{
var command = new SqlCommand;
var builder = new StringBuilder(@"
SELECT Id, CreateDate
FROM Post
WHERE 1 = 1");
if (startDate.HasValue)
{
builder.Append(@"
AND CreateDate >= @startDate");
command.Parameters.AddWithValue("@startDate", startDate.Value);
}
if (endDate.HasValue)
{
builder.Append(@"
AND CreateDate <= @endDate");
command.Parameters.AddWithValue("@endDate", endDate.Value);
}
command.CommandText = builder.ToString;
return command.GetEnumerable<T001>
}

interface T001
{
int Id {get;}
DateTime? CreateDate {get;}
}

kokoc88

Отлично! Ты захватил наживку.
А ты опасный парень, как я погляжу! Ждём, когда ответы на твои посты будут ставить на комп трояны.
Найди отличие от ванильного C#:
Блядь, а это что?
interface T001

6yrop

Блядь, а это что?
code:
interface T001
Это тип, который возвращает метод XxxQuery.

kokoc88

Это тип, который возвращает метод XxxQuery.
А 153-ий метод будет возвращать T0153?

6yrop

А 153-ий метод будет возвращать T0153?
Название типа на усмотрение автора кода.

kokoc88

Название типа на усмотрение автора кода.
А зачем ты тогда выбираешь такое говнецо?

6yrop

А зачем ты тогда выбираешь такое говнецо?
Это известная проблема:

here we face our first problem, in that an object-oriented system like C# or Java cannot return just "parts" of an object--an object is an object, and if the Person object consists of 12 fields, then all 12 fields will be present in every Person returned. This means that the system faces one of three uncomfortable choices: one, require that Person objects must be able to accomodate "nullable" fields, regardless of the domain restrictions against that; two, return the Person completely filled out with all the data comprising a Person object; or three, provide some kind of on-demand load that will obtain those fields if and when the developer accesses those fields, even indirectly, perhaps through a method call.
(Note that some object-based languages, such as ECMAScript, view objects differently than class-based languages, such as Java or C# or C++, and as a result, it is entirely possible to return objects which contain varying numbers of fields. That said, however, few languages possess such an approach, not even everybody's favorite dynamic-language poster child, Ruby, and until such languages become widespread, such discussion remains outside the realm of this essay.)
http://blogs.tedneward.com/2006/06/26/The+Vietnam+Of+Compute...

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

Dasar

Поэтому в качестве имени использую числовой индекс.
Имхо, следующий вариант нагляднее: Post_Sub0001. Означает, что данный структурный тип является подмножеством структурного типа Post

kokoc88

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

6yrop

Проблема-то может и известная, но решение выбрано такое, что меркнут все вместе взятые недостатки предыдущих.
Какие недостатки у четвертого варианта?

6yrop

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

That sure seems like a lot of boilerplate code to project out a subset of the fields. It’s also often unclear what to name such a type. Is CustomerTuple really a good name? What if we had projected out Name and Age instead? That could also be a CustomerTuple. So, the problems are that we have boilerplate code and it doesn’t seem that there are any good names for the types that we create. Plus, there could also be many different types required, and managing them could quickly become a headache.
MSDN Magazine 2007 June
 

Проблемы boilerplate кода у меня нет, поскольку T001 генерируется при первом запуске тестов.
А имя типа близко к отсутствию имени.

kokoc88

Какие недостатки у четвертого варианта?
А ты сам не понимаешь? В любом случае это бесполезно тебе объяснять, потому что ты упорот.

6yrop

А ты сам не понимаешь? В любом случае это бесполезно тебе объяснять, потому что ты упорот.
Распиши, попытаюсь понять.

kokoc88

Проблемы boilerplate кода у меня нет, поскольку T001 генерируется при первом запуске тестов.
Щито? У тебя там больше половины кода - boilerplate. Но этого ты тоже не понимаешь.

kokoc88

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

6yrop

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

kokoc88

Давай ты не будешь указывать что мне делать? Оставь свои привычки пастуха для своих баранов на работе.
У тебя что, только что прогорел стул? Или пламя прожгло пол и добалось до соседей?
Я тебе подкину такую идею - пойди напиши на меня ещё одну докладную в разделе Forum. Тему можно назвать так: "Пользователь форумлокал сказал мне подумать". Но пукан перед этим остуди, а то устроишь пожар почище 1812 года.

6yrop

Имхо, следующий вариант нагляднее: Post_Sub0001. Означает, что данный структурный тип является подмножеством структурного типа Post
Проекция может быть из нескольких таблиц. Это чистой воды анонимный тип: просто множество полей. Не требуется заключать в имя типа какую-либо информацию.
На твой пост можно сослаться как #Post12445174. Аналогично, используя T001, мы просто ссылаемся на тип.

stm5872449

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

6yrop

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

T123 x = ...
Method1(x.As(default(T321;

Method1(T321 y) {...}

kokoc88

Method1(x.As(default(T321;
Отличная ваниль, правда уже отдаёт говном. Осталось ещё посмотреть на примеры кода, когда чукча писатель. В смысле когда тип T4321 состоит из данных, которые в двух других типах, плюс ещё какие-то новые значения, которые нужно вычислить.

6yrop

Отличная ваниль, правда уже отдаёт говном.

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

Типы T001 это типы результатов выборки из БД. Вычислениями занимаются другие типы.

kokoc88

Покажи код, который лучше.
У тебя даже пример, который специально натянут на ко-ку, и который не решает ни одной задачи полностью, выглядит хуже обычного ADO.NET
Вызываешь метод As с двумя аргументами.

С какими? Названия свойств не пересекаются.
Типы T001 это типы результатов выборки из БД. Вычислениями занимаются другие типы.
Ага, после ванили со вскусом говна начинаются искусственные ограничения. То есть ко-ку за все эти годы так и не научилась решать банальные задачи? Выгрузить объект из БД, поменять поле, записать обратно? Выгрузить два объекта, сделать из этих данных третий, записать? То есть это не просто убогое говно с вырвиглазным стилем кода, но ещё и за все эти годы оставшееся абсолютно бесполезным?

6yrop

С какими? Названия свойств не пересекаются.
Method1(x.As(y, default(T321;
Реальным неудобством метода As является то, что нужно запускать дополнительный find usages. Стандартный плюс дополнительный. Это не удобно, да.

6yrop

У тебя даже пример, который специально натянут на ко-ку, и который не решает ни одной задачи полностью, выглядит хуже обычного ADO.NETВызываешь метод As с двумя аргументами. С какими? Названия свойств не пересекаются.Типы T001 это типы результатов выборки из БД. Вычислениями занимаются другие типы.Ага, после ванили со вскусом говна начинаются искусственные ограничения. То есть ко-ку за все эти годы так и не научилась решать банальные задачи? Выгрузить объект из БД, поменять поле, записать обратно? Выгрузить два объекта, сделать из этих данных третий, записать? То есть это не просто убогое говно с вырвиглазным стилем кода, но ещё и за все эти годы оставшееся абсолютно бесполезным?
Пока ты не напишешь код (и будет с чем сравнить твои слова банальная ложь.

kokoc88

Method1(x.As(y, default(T321;
И как это поменяет тип T312 { string Name; int Age; } на T647 { int AgeInHours; string ShortName; string LongName; }?

6yrop

И как это поменяет тип T312 { string Name; int Age; } на T647 { int AgeInHours; string ShortName; string LongName; }?
Я думал, что идет речь о вот таком условии задачи:
T001 x = ...
T001 {string Name; int Age;}
T002 y = ...
T002 {int AgeInHours; string ShortName; string LongName;}
Выходной тип:
T312 { string Name; string ShortName; string LongName; }

kokoc88

Я думал, что идет речь о вот таком условии задачи:
T001 x = ...T001 {string Name; int Age;}
T002 y = ...T002 {int AgeInHours; string ShortName; string LongName;}
Выходной тип:T312 { string Name; string ShortName; string LongName; }
И что предлагается для решения задачи с этим условием?

6yrop

И что предлагается для решения задачи с этим условием?
Типы Txxx это интерфейсы. В рантайме генерируется реализация T312, которая перенаправляет вызовы к x и y.
В тестах в сборке находятся все вызовы метода As, смотрим какие generic параметры и проверяем совместимость. Аналогичным образом реализуем find usages.

kokoc88

Типы Txxx это интерфейсы. В рантайме генерируется реализация T312, которая перенаправляет вызовы к x и y.
Как она генерируется, где ты указал, что у неё всего три конкретных свойства?

6yrop

Как она генерируется, где ты указал, что у неё всего три конкретных свойства?
В методе As генерируется реализация интерфейса TResult.
 

class Program
{
    void Method0(T001 x, T002 y)
    {
     Method1(x.As(y, default(T312;
    }

    void Method1(T312 z)
    {
     throw new NotImplementedException;
    }
}

public interface T001
{
    string Name { get; }
    int Age { get; }
}

public interface T002
{
    int AgeInHours { get; }
    string ShortName { get; }
    string LongName { get; }
}

public interface T312
{
    string Name { get; }
    string ShortName { get; }
    string LongName { get; }
}

public static class Util
{
    public static TResult As<T1, T2, TResult>(this T1 arg1, T2 arg2, TResult resultPrototype)
    {
     ...
    }
}

kokoc88

В методе As генерируется реализация интерфейса TResult.
И как это работает с условиями, которые озвучивались выше? Например, "Названия свойств не пересекаются." То есть всё сломается, если у меня AgeInYears и Age?

6yrop

И как это работает с условиями, которые озвучивались выше? Например, "Названия свойств не пересекаются." То есть всё сломается, если у меня AgeInYears и Age?
Кто тебя занет, о каком пересечении ты говоришь. Может ты меешь ввиду, что нет пересечений между T001 и T002.
То есть всё сломается, если у меня AgeInYears и Age?

Статическая проверка не пройдет.

kokoc88

Статическая проверка не пройдет.
То есть мы пришли к ещё одному ограничению твоей говнотехнологии.

kokoc88

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

6yrop

То есть мы пришли к ещё одному ограничению твоей говнотехнологии.
Еще одного ограничения нет. Опять обманываешь.

6yrop

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

kokoc88

Не о чем говорить, потому что ты не формулируешь задачу. Напиши нормальное условие задачи.
Выгружаются два списка, каждый из которых состоит из объектов с разными названиями полей. В твоём стиле пусть это будет: T1000 { int Id; string Name; int Age; int Version; } и T0047 { string Name; string BlobText; int Val; } Для этой задачи будем считать, что Id - это primary key, а пересечение по Name не выгодно для выгрузки с помощью JOIN даже при условии последующей фильтрации.
Нужно создать список из объектов T3000 { int Id; string BlobPreview; int AdjustedVal; }, который получается объединением T1000 и T0047 по Name, с фильтром когда BlobText содержит некоторую подстроку, BlobPreview является подстрокой из первых 100 символов, а AdjustedVal равен Age + Val.
Типы T1000 и T3000 передаются в стороннюю библиотеку. Названия их свойств не совпадают с названием столбцов в БД. В частности в нашем примере T1000 хранится в NameAgeVersionTable (Id, ShortName, VersionRenamed а T3000 хранится в PreviewTable (Id, BlobPreviewRenamed, AdjustedValRenamed)
После создания списка T3000 происходит запрос, который делает MERGE INTO PreviewTable (которая для T3000) USING (VALUES...) ON ...Id = ...Id WHEN NOT MATCHED BY TARGET INSERT, иначе UPDATE полей BlobPreviewRenamed и AdjustedValRenamed
После этого запроса сразу же идёт запрос, который делает UPDATE NameAgeVersionTable (которая для T1000) SET VersionRenamed = VersionRenamed + 1 для всех Id из списка обновления по T3000
Все запросы выполняются в одной транзакции с уровнем изоляции RepeatableRead. Запросы должны выполняться батчами, то есть для обычного SQL Server 2012 + ADO.NET внутри USING VALUES содержит как минимум по 300 значений. То же касается, например, условия Id IN (...) для T1000.

6yrop

хотя бы одну реальную задачу.

Честно говоря, я мало что понял из твоего потока сознания, увидел лишь признаки реально задачи:
1. Типы T1000 и T3000 передаются в стороннюю библиотеку.
2. VersionRenamed, BlobPreviewRenamed, AdjustedValRenamed.
Поскольку подача материала и при первом прочтении сам материал меня не привлек, то займусь им, если будет свободное время. Надо продираться через твой поток сознания.
P.S. И да, если ссылаешься на мой стиль, то, пожалуйста, не перевирай его. У меня используется три цифры, больше тысячи типов я не использую. Поправь, пожалуйста, либо убери ссылку на мой стиль. Иначе я этим заниматься не буду.

kokoc88

Поскольку подача материала и при первом прочтении сам материал меня не привлек, то займусь им, если будет свободное время. Надо продираться через твой поток сознания.
Слив засчитан.

6yrop

Слив засчитан.
Буду надеяться, что ты больше не будешь проявлять интереса к Controllable Query.

kokoc88

Буду надеяться, что ты больше не будешь проявлять интереса к Controllable Query.
Не льсти себе, к этому говну никто не проявляет никакого интереса. Тебя просто лениво и зачастую весьма жЫрно потролливают. Правда в последнее время ты генерируешь всё меньше лулзов и всё больше клиники...

6yrop

Тролинг это когда весело.
Ты думаешь от вот этого весело кому то?
Выгружаются два списка, каждый из которых состоит из объектов с разными названиями полей. В твоём стиле пусть это будет: T1000 { int Id; string Name; int Age; int Version; } и T0047 { string Name; string BlobText; int Val; } Для этой задачи будем считать, что Id - это primary key, а пересечение по Name не выгодно для выгрузки с помощью JOIN даже при условии последующей фильтрации.
Нужно создать список из объектов T3000 { int Id; string BlobPreview; int AdjustedVal; }, который получается объединением T1000 и T0047 по Name, с фильтром когда BlobText содержит некоторую подстроку, BlobPreview является подстрокой из первых 100 символов, а AdjustedVal равен Age + Val.
Типы T1000 и T3000 передаются в стороннюю библиотеку. Названия их свойств не совпадают с названием столбцов в БД. В частности в нашем примере T1000 хранится в NameAgeVersionTable (Id, ShortName, VersionRenamed а T3000 хранится в PreviewTable (Id, BlobPreviewRenamed, AdjustedValRenamed)
После создания списка T3000 происходит запрос, который делает MERGE INTO PreviewTable (которая для T3000) USING (VALUES...) ON ...Id = ...Id WHEN NOT MATCHED BY TARGET INSERT, иначе UPDATE полей BlobPreviewRenamed и AdjustedValRenamed
После этого запроса сразу же идёт запрос, который делает UPDATE NameAgeVersionTable (которая для T1000) SET VersionRenamed = VersionRenamed + 1 для всех Id из списка обновления по T3000
Все запросы выполняются в одной транзакции с уровнем изоляции RepeatableRead. Запросы должны выполняться батчами, то есть для обычного SQL Server 2012 + ADO.NET внутри USING VALUES содержит как минимум по 300 значений. То же касается, например, условия Id IN (...) для T1000.

6yrop

Кстати, напиши свой поток сознания на C#, так будет понятнее.
И потом код всё равно нужен для сравнения.

kokoc88

Тролинг это когда весело.Ты думаешь от вот этого весело кому то?
Деточка, троллинг - это когда весело троллю.

kokoc88

Кстати, напиши свой поток сознания на C#, так будет понятнее. И потом код всё равно нужен для сравнения.
Ты уже слился, зачем мне теперь что-то писать?

marat7256

Может и не весело, но в общих чертах вполне понятно.

6yrop

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

6yrop

Может и не весело, но в общих чертах вполне понятно.
В общих чертах там проблем нет, решение очевидно в общих чертах. Распишите детали на C#.

6yrop


Вау, нас еще кто-то читает.

6yrop

Распишите детали на C#.
Если я и буду этим заниматься, то сначала сделаю код на ADO.NET без CQ. Для того чтобы согласовать с Майком, что он именно это имел в виду. А потом добавлю тесты посредством CQ.
До тех пор пока никто не написал вариант на ванильном ADO.NET говорить о том, что CQ плох или хорош смысла нет.

kokoc88

Подожди, а зачем мне вестись на тролинг?
Я не знаю, зачем ты на него ведёшься. Я думаю, что ты клоун по жизни, и призван радовать читателей Society и Development.

kokoc88

Для того чтобы согласовать с Майком, что он именно это имел в виду.
Да не оправдывайся, и так понятно, что ты слился. Задачка-то тривиальная, на семействах ORM light и Dapper решается за 15 минут, если не проверять корректность. А её и не планировалось проверять, мы же оцениваем ограничения ко-ку.

khachin

нас еще кто-то читает.
Я еще иногда почитываю. Через страницу (у меня на страницу по 40 постов).
Неконструктивная в теме беседа. Диалог нептуниста и плутониста.

6yrop

ORM light и Dapper решается за 15 минут
Приведи код.
CQ отработает как положено.

6yrop

Неконструктивная в теме беседа.
Когда у меня с Майком была конструктивная беседа? Майк пытается за счет меня само утверждаться. И у него это все время не выходит.

kokoc88

CQ отработает как положено.
Ага, ага, но код написать ты не осилил... Конечно, конечно...

6yrop

Ага, ага, но код написать ты не осилил... Конечно, конечно...
Именно код не осилил написать ты. А для меня основное препятствие не код написать, а детально разобраться в твоем потоке сознания.

kokoc88

Именно код не осилил написать ты.
На нормальной технологии там писать-то нечего. Будет 3 DTO класса и 4 метода.
А для меня основное препятствие не код написать, а детально разобраться в твоем потоке сознания.
Заметь, Бобровников всё понял, а наш супер-герой, у которого типа скиллы по работе с кодом, уже час ноет. В моей задаче каждый пункт делается в 2-3 строки, кроме основного MERGE. Наверное, ты просто не знаешь SQL для SQL Server.

6yrop

Напиши код. Или сам не понимаешь что написал? :grin:
Заметь, Бобровников всё понял, а наш супер-герой, у которого типа скиллы по работе с кодом, уже час ноет. В моей задаче каждый пункт делается в 2-3 строки, кроме основного MERGE. Наверное, ты просто не знаешь SQL для SQL Server.

Бобровников понял ВСЁ, а я ничего не знаю. :grin: Раз он понял всё пусть напишет код.

kokoc88

Напиши код. Или сам не понимаешь что написал?
Да ты не волнуйся, я уже написал. И в контексте полного слива ко-ку я запощу свой код.

6yrop

Ну так пости.

kokoc88

Ну так пости.
Нееет. Сначала ты напишешь, что ты слился.

6yrop

Нееет. Сначала ты напишешь, что ты слился.
Я ж не ты, ложь писать не привык.

marat7256

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

kokoc88

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

6yrop

А в чем смысл этого?
Майк хочет показать, что CQ не отработает проверки. Какая разница кто первый запостит код?
Кое какие проверки может и не отработают автоматически, но CQ это никогда и не заявлял. CQ всегда позиционировался как помощник в написании тестов.

6yrop

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

kokoc88

Майк хочет показать, что CQ не отработает проверки. Какая разница кто первый запостит код?
Какие проверки? До проверок дело не дошло. У тебя три типа с разными названиями свойств не работают. О чём ты сам сказал выше. И вдруг "Еще одного ограничения нет. Опять обманываешь." Вот я и дал тебе задачку, где имена столбцов отличаются от имён свойств, а типы надо собрать из других типов и не только читать, но и записывать в базу.

kokoc88

Прикинь, я не слился.
Нет, нет, ты именно что слился. Это уже понятно. Может быть, если бы ты попытался написать хоть какой-то код, это бы немного сгладило твой слив. Но ты не можешь этого сделать. Хотя на семействе orm light / dapper там 100 строк кода брутто, включая boilerplate.

6yrop

orm light / dapper там 100 строк кода брутто, включая boilerplate.
которые ты не осилил запостить :grin:

kokoc88

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

6yrop

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

pilot

Вы два ушлепка. Но для постановки задачи ты мог бы написать нормальный код, а шурик - переделать его с CQ. Потому как по твоей постановке неясно что ты имел в виду.
Так что пока ты сливаешься.

kokoc88

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

kokoc88

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

6yrop

Всем читателям этого треда, давайте попросим Майка запостить код. Разве вам не интересно как сольется CQ?

6yrop

Бля, сегодня никакой работу. :( Пойду поработаю.

pilot

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

marat7256

Код от Майка получен. Жду Шурика.

6yrop

А обещал 15 минут. :smirk:
Я должен продемонстрировать, как CQ проверяет запросы, поэтому сначала надо получить код от Майка. Пусть будет код, а то Майк опять начнет словоблудить с условием задачи.

marat7256

Код пришел давно (17:18). Просто я был не у компа - ездил забирать авто из ремонта.
Считаю, что ты должен написать свой код без его.

6yrop

Код пришел давно. Просто я был не у компа - ездил забирать авто из ремонта.
А зачем ты вообще нужен?
Майк мог выложить zip архив с паролем, а пароль выложить позже.
Не вижу смысла писать мне код, пока я не увижу, что хочет Майк.

6yrop

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

6yrop

Считаю, что ты должен написать свой код без его.
Какова причина, почему Майк не выкладывает код? Что он хочет показать?

marat7256

То, что хочет Майк, вполне поняно написано в его посте с постановкой задачи. Конечно, могут быть некоторые разночтения, но явно не сильно существенные. Поскольку я совсем не бум-бум в С#, то ни написать ни проверить не могу. Так что я самый классный smart-zip-архив.
Все. Более дискутировать не буду. Получу код от тебя, выложу оба.

marat7256

Мне кажется, просто интересно сравнить ваши компетенции независимым образром.
При этом лично мне не важно, что конкретно хочет показать Майк.

pilot

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

kokoc88

Мы же не раз это с тобой обсуждали.
Обсуждали? Вряд ли с тобой можно что-нибудь обсуждать. Я бы сказал, что мы определённо перекидывались постами, но этот процесс вряд ли можно назвать обсуждением.

kokoc88

Конечно, могут быть некоторые разночтения, но явно не сильно существенные.
Нет, разночтения могут быть вполне существенными. К постановке задачи придраться намного легче, чем к решению. Но постановку всегда можно уточнить.
Например, "Типы T1000 и T3000 передаются в стороннюю библиотеку". Можно уточнить, что из-за этого нельзя менять названия свойств, типы должны быть ровно такими, как описаны. Можно уточнить, что тип T0047 хранится в таблице на усмотрение решающего, потому что в условии про это не написано.
Можно сразу сказать, что объединение T1000 и T0047 по Name выполняется через LINQ Join. Вообще всё условие для построения списка из T3000 - это LINQ Where и Join. Можно написать формально и с ошибками, это не главная часть задачи. Здесь важно посмотреть, как тип T3000 создаётся в рантайме.
Условие про MERGE можно переформулировать попроще. В конце концов это стандартная операция, можно было бы написать так: для полученного списка из T3000 нужно выполнить MERGE по Id, который либо заканчивается INSERT, либо UPDATE всех полей.
Можно уточнить, что в транзакцию достаточно поместить только две последние операции записи. Это действительно серьёзная неточность в моём условии. Впрочем, переделать код одинаково не составит труда.
Последнее условие про батчинг означает, что операции MERGE и UPDATE может не получиться выполнить одним запросом, поэтому список из T3000 нужно разбить на куски или использовать другой подход. (Кстати, как оказалось, в моём фреймворке это не встроено, поэтому моё решение содержит ошибку, и к нему нужно будет добавить пару строк.)
В общем, уточнить можно всё и всегда. Вместо этого в ответ написан пост, за который просто засчитывается слив.

marat7256

Хочу уточнить две вещи: ждать ли мне код от Шурика, и выкладывать ли код Майка, если от Шурика ничего не будет?

kokoc88

Хочу уточнить две вещи: ждать ли мне код от Шурика, и выкладывать ли код Майка, если от Шурика ничего не будет?
Я могу и сам выложить, там стесняться нечего. Но тогда давай я это сделаю в новой теме. :)

pilot

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

kokoc88

Да, я тоже помню что ты тогда очень тупил. Я на тебе совсем уж было крест поставил, а вишь, оказывается, это могло быть досадное недоразумение. Пости код, реабилитируйся.
Ты поставил на мне крест? Это приятный комплимент от человека, который изображает из себя гиганта мысли и знания, а на деле обладает интеллектом обычного деревенского Ваньки, который постоянно ищет куда бы ему сунуться, чтобы нос не прищемили.

pilot

Ты поставил на мне крест? Это приятный комплимент от человека, который изображает из себя гиганта мысли и знания, а на деле обладает интеллектом обычного деревенского Ваньки, который постоянно ищет куда бы ему сунуться, чтобы нос не прищемили.
Как же так, тебя оказывается можно загнобить даже с деревенским интеллектом :confused:

kokoc88

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

pilot

С деревенским интеллектом можно загнобить только себя, но вряд ли потом получится это понять.
Давай ссылку на ту историю, помогу, объясню :confused:

kokoc88

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

pilot

Мне не нужны объяснения деревенского дурачка, и тем более его помощь.
Ок, передам . Мое предложение в силе.
Вернемся к нашим двум баранам: Так что там с кодом? Ты слился или как?

kokoc88

Вернемся к нашим двум баранам: Так что там с кодом? Ты слился или как?
Ты хочешь поучаствовать? Тогда решай задачу. Если нет, то предлагаю тебе проследовать на хуй.

pilot

Вынужден отклонить ваше предложение. Можете сбежать с шуриком в приват, поджав хвост. Иначе страдайте.

kokoc88

Вынужден отклонить ваше предложение. Можете сбежать с шуриком в приват, поджав хвост. Иначе страдайте.
Какой грозный вояка! Послан на хуй, но не сдаётся и потрясает кулачками.

pilot

Неплохое начало.

stm5643616

Но пукан перед этим остуди, а то устроишь пожар почище 1812 года.

6yrop

 

новый инпут, который сразу работает с отпарсенными данными (у меня там intInput, который принимает и возвращает Maybe Int).
 

Я тут вдруг вспомнил, что твой новый инпут это фактически мой метод расширения для текстбокса Option<int> GetInt32(this TextBox it)
Результат совпал. Только у тебя обоснование не убедительное "загрязнять доменную модель". А у меня четкое обоснование — кешируем результат функции int.Parse. В реальности такое кеширование вряд ли понадобится.

luna89

Я правильно понимаю что в дотнете текстовый инпут одновременно является хэштаблицей, в которую кто угодно может пихать что угодно?

Dasar

Я правильно понимаю что в дотнете текстовый инпут одновременно является хэштаблицей, в которую кто угодно может пихать что угодно?
У input-а есть одно поле, в которое кто угодно может пихать что угодно. Если договориться, что в этом поле будет хэштаблица, то тогда кто угодно сможет хранить что угодно под своим ключем.

luna89

Тем временем, в typescript 1.6 заявлена поддержка синтаксиса jsx.
Хотя я считаю, что jsx - ненужный синтаксический наворот, но оцениваю новость положительно - она приближает окончательное доминирование реакта.
Оставить комментарий
Имя или ник:
Комментарий: