flatgui: reactive gui toolkit implemented in clojure
TRANSCRIPT
Reactive GUI Implemented in Clojure
Denys Lebediev
Quick background
«Традиционный» подход
• Шаблон Event-Listener
• Модель данных виджета мутируема
– Example: isPressed/setPressed
• Состояние виджета мутируемо
– Example: isEnabled/setEnabled
Традиционный подход: проблемы
• Сложные GUI контейнеры (диалоги, мастера, и т.д.) бывает трудно:
– Разрабатывать
– Поддерживать
– Расширять
• Почему?
Традиционный подход: проблемы
• Таким способом мы прячем логику
Традиционный подход: проблемы
• Таким способом мы прячем логику
– Пример: если кнопка стала неактивной, то может быть неясно почему она так сделала, и когда, при каких условиях с ней это происходит
Традиционный подход: проблемы
• Таким способом мы прячем логику
– Пример: если кнопка стала неактивной, то может быть неясно почему она так сделала, и когда, при каких условиях с ней это происходит
• А также, рассредоточиваем логику
Традиционный подход: проблемы
• Таким способом мы прячем логику
– Пример: если кнопка стала неактивной, то может быть неясно почему она так сделала, и когда, при каких условиях с ней это происходит
• А также, рассредоточиваем логику
– Причины произошедшего с кнопкой могут быть разбросаны по разным местам исходного кода приложения
Традиционный подход: проблемы
• Таким способом мы прячем логику
– Пример: если кнопка стала неактивной, то может быть неясно почему она так сделала, и когда, при каких условиях с ней это происходит
• А также, рассредоточиваем логику
– Причины произошедшего с кнопкой могут быть разбросаны по разным местам исходного кода приложения
– Тем более, со временем
Традиционный подход: проблемы
• Incidental complexity– В результате мы пишем код, несущественный
для исходной задачи (домена)
– Тратим ресурсы на то чтобы решить когда
– А также, на то чтобы разобраться в решениях коллег
• Трудности автоматизированного тестирования– На основе робота
Есть другой способ
• Библиотека берёт часть рутины на себя
– Однажды реализована и оттестирована
– Стремимся к тому, чтобы разработчик концентрировался на своей задаче (домене)
Есть другой способ
• Библиотека берёт часть рутины на себя
– Однажды реализована и оттестирована
– Стремимся к тому, чтобы разработчик концентрировался на своей задаче (домене)
• Вся логика одного свойства в одном месте
Есть другой способ
• Библиотека берёт часть рутины на себя
– Однажды реализована и оттестирована
– Стремимся к тому, чтобы разработчик концентрировался на своей задаче (домене)
• Вся логика одного свойства в одном месте
• Обычные юнит-тесты для кода, который управляет моделью и состоянием виджета
Hello world with FlatGUI
(defcomponent checkbox :say-hello {:text "Greeting"})
(def nogreeting-text "Greeting not provided")
(def greeting-text "Hello, world!")
(defevolverfn greeting-evolver :text
(if (get-property [:say-hello] :pressed)
greeting-text
nogreeting-text))
(defcomponent label :greeting-label
{:text nogreeting-text
:evolvers {:text greeting-evolver}})
Hello world with FlatGUI
(defcomponent checkbox :say-hello {:text "Greeting"})
(def nogreeting-text "Greeting not provided")
(def greeting-text "Hello, world!")
(defevolverfn greeting-evolver :text
(if (get-property [:say-hello] :pressed)
greeting-text
nogreeting-text))
(defcomponent label :greeting-label
{:text nogreeting-text
:evolvers {:text greeting-evolver}})
Hello world with FlatGUI
(defcomponent checkbox :say-hello {:text "Greeting"})
(def nogreeting-text "Greeting not provided")
(def greeting-text "Hello, world!")
(defevolverfn greeting-evolver :text
(if (get-property [:say-hello] :pressed)
greeting-text
nogreeting-text))
(defcomponent label :greeting-label
{:text nogreeting-text
:evolvers {:text greeting-evolver}})
Чем это отличается?
• greeting-evolver является единым место для всей логики свойства :text лейбла
Чем это отличается?
• greeting-evolver является единым местом для всей логики свойства :text лейбла
• Эта функция содержит логику, не рутину
Чем это отличается?
• greeting-evolver является единым место для всей логики свойства :text лейбла
• Эта функция содержит логику, не рутину
• Эту функцию легко отлаживать
Чем это отличается?
• greeting-evolver является единым место для всей логики свойства :text лейбла
• Эта функция содержит логику, не рутину
• Эту функцию легко отлаживать
• Эту функцию легко покрыть юнит-тестами
Чем это отличается?
• greeting-evolver является единым место для всей логики свойства :text лейбла
• Эта функция содержит логику, не рутину
• Эту функцию легко отлаживать
• Эту функцию легко покрыть юнит-тестами
• Разработчику не нужно решать когда её вызвать
Чем это отличается?
• greeting-evolver является единым место для всей логики свойства :text лейбла
• Эта функция содержит логику, не рутину
• Эту функцию легко отлаживать
• Эту функцию легко покрыть юнит-тестами
• Разработчику не нужно решать когда её вызвать
• Разработчику не нужно её вызывать
Чем это отличается?
• Реактивный движок берёт эти заботы на себя
– С каждым новым GUI контейнером, как бы ни отличалась его предметная область, по сути мы решаем одни и те же задачи
– Однажды реализованный движок прячет их под капот
– А также, покрывает тестами
FlatGUI architecture
Engine performs state transition
Option to run as RIA
Setup collaboration sessions
Опыт использования Clojure
• Макросы здесь очень кстати
– Создание типов и объектов виджетов, property evolver -функций
– Layout management: “ниже” “справа” и т.п.
– Возможностей IDE ещё немного недостаточно
• Было много профилирования
– Появилось немного не-идиоматического Clojure кода
Что дальше?
• Focus-management – первое, чего не хватает, и что скоро появится
• Оптимизации для сетевого варианта
• Писать тесты и документацию
• Доводить имеющиеся виджеты и делать новые
• Пожелания приветствуются
• Участие приветствуется