Душа поэта не вытерпела безвестности, и он щедро делится своими высокими идеями. (с) анонимус


WTF per hour
Некоторое время назад я написал три статьи «Архитектурные решения для мобильной игры» посвящённые архитектуре моей мечты:
Часть 1: Model
Часть 2: Command и их очереди
Часть 3: View на реактивной тяге

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

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

Есть такое понятие «Зона ближайшего развития»: Она определяется содержанием тех задач, которые человек ещё не может решить самостоятельно, но способен решить в совместной деятельности с тем, кто уже взял этот барьер. То, что изначально доступно для человека при участии других, становится затем его собственным достоянием (навыками, умениями). Поэтому полезнее всего, конечно, совместно с сильным лидом работать над интересным проектом. Но где ж на всех проектов найти, а уж лидов тем более.

В общем, подумав во всё вот это я захотел попробовать делиться с людьми в другом формате: Я устрою стрим, посвящённый Code Review, буду показывать свой код, посвящённый реактивности и объяснять те идеи, которые в нём заложены и почему написано именно так, а не иначе. Стрим продлится ровно час. Здесь в статье я бегло опишу о чём я хочу поговорить, а потом постфактум добавлю хронометраж и какие вопросы удалось обсудить и с какой минуты.

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

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

Стрим начнётся в среду в 22:30 (вот такое у меня свободное время, увы. :) и будет доступен вот тут:


Теперь собственно о содержании стрима.


Вообще ничёшная реализация реактивности есть в библиотеке UniRX с которой я всячески рекомендую знакомиться. Возможно вы её буквально возьмёте себе, а может быть просто натырите оттуда идей. Но я буду показывать свой собственный велосипед, написанный с нуля. Это не только потому что я люблю велосипеды. В UniRX, реализует стандартные интерфейсы System. IObserver<in T> и System.IObservable<out T>. Причём во многих местах делает это ThreadSafe (не всегда правильно) и internal. То есть библиотека имеет много лишнего кода, и её неудобно расширять. А расширять и приспосабливать под местные условия в реальности мне понадобилось в трех случаях из трёх.

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

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

Кроме того, проблему из себя представляет сам IObserver. Во-первых, в нем есть на мой вкус совершенно лишний метод OnError(Exception e). Когда у вас многпоточность в этом есть глубокий смысл, заключающийся в том, чтобы вывалить этот эксепшен в UnityThread и он не остался незамеченным. А ещё изначально этот интерфейс был придуман для работы с файлами, которые сплошь и рядом падают с ошибками. Но в однопоточности и когда вы работаете с моделью приложения это лишний гемор на ровном месте, я предпочитаю, чтобы код поднял тревогу именно в том месте, где он сдох.

Вторая проблема IObserver состоит в том, что я хочу транзактность изменений. Просто представьте, что из одного источника вам приходит List, а из другого потока мы получаем индекс элемента, который мы должны из листа вынуть и передать дальше. И вот индекс указывает на последний элемент, и вот один элемент удаляется, а индекс уменьшается на 1. В конечном счёте всё будет прекрасно и результат операции не изменится, но если к нам придёт сообщение об изменении List и только потом сообщение о смене индекса, наш код поймает IndexOutOfRangeException. На самом деле та же проблема с порядком применения изменений может проявляться ещё десятками способов, этот просто самый очевидный.

Поэтому я хочу свой интерфейс, с одной стороны без протаскивания OnError но зато с другой стороны содержащий .OnTransaction(ITransaction t)

Что-то я чувствую, что слишком углубляюсь. Проговорить об этом во время стрима с кодом на экране будет явно быстрее, и на много понятнее. Дальше совсем о верхам:

  • Мои интерфейсы IActive и IReactive. Чем они прекраснее всяких event-ов и как выглядит конечный результат их использования.
  • ActiveProperty<T>. Чем отличается от Active.Proxy<T>, как передаётся и как обрабатывается начальное значение переменной.
  • Как я всё это проверяю тестами и почему это очень удобно. На самом деле нормально написать такую штуку без тестов вообще не получилось бы.
  • Стратегия уборки за собой IDisposible. Двойной механизм, поддерживающий и OnComplete и ICollection<IDisposible>
  • Как легко делать расширения потокового инструментария и чтение самых полезных примеров.
  • Инструменты отладки всего этого безобразия. В первую очередь .Log(), а до CalculationTree дойдём как-нибудь в слекдующий раз.
  • Внимательное чтение кода Active.Proxy<T>, почему там под капотом не какие-нибудь эвенты, а двусвязный список.
  • IActiveList и IActiveDictionary, сначала самые общие идеи.
  • Как производится разделение на ViewModel, Controller и View. Как не зациклить свои стримы.
  • Процесс проваливания переменных из реактивности в модель.
  • ActiveList и ActiveDictionary с минимальной дельтой. Отличие от лобовой реализации DeltaDictionary вообще и в UniRX в частности.
  • Общая стратегия исправления ошибок в коде, потому что нет такого предмета в вузах, а должен быть.

На самом деле тут уже на несколько часов рассказов и показов кода, так что давайте начнём с первого часа, а дальше как попрёт.

P.S. Это будет мой первый стрим, так что не судите строго если там какие-нибудь технические накладки случатся.

Комментарии (0)