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 – первое, чего не
хватает, и что скоро появится
• Оптимизации для сетевого варианта
• Писать тесты и документацию
• Доводить имеющиеся виджеты и делать
новые
• Пожелания приветствуются
• Участие приветствуется
info@flatgui.org

FlatGUI: Reactive GUI Toolkit Implemented in Clojure

  • 1.
    Reactive GUI Implementedin Clojure Denys Lebediev
  • 2.
  • 3.
    «Традиционный» подход • ШаблонEvent-Listener • Модель данных виджета мутируема – Example: isPressed/setPressed • Состояние виджета мутируемо – Example: isEnabled/setEnabled
  • 4.
    Традиционный подход: проблемы •Сложные GUI контейнеры (диалоги, мастера, и т.д.) бывает трудно: – Разрабатывать – Поддерживать – Расширять • Почему?
  • 5.
    Традиционный подход: проблемы •Таким способом мы прячем логику
  • 6.
    Традиционный подход: проблемы •Таким способом мы прячем логику – Пример: если кнопка стала неактивной, то может быть неясно почему она так сделала, и когда, при каких условиях с ней это происходит
  • 7.
    Традиционный подход: проблемы •Таким способом мы прячем логику – Пример: если кнопка стала неактивной, то может быть неясно почему она так сделала, и когда, при каких условиях с ней это происходит • А также, рассредоточиваем логику
  • 8.
    Традиционный подход: проблемы •Таким способом мы прячем логику – Пример: если кнопка стала неактивной, то может быть неясно почему она так сделала, и когда, при каких условиях с ней это происходит • А также, рассредоточиваем логику – Причины произошедшего с кнопкой могут быть разбросаны по разным местам исходного кода приложения
  • 9.
    Традиционный подход: проблемы •Таким способом мы прячем логику – Пример: если кнопка стала неактивной, то может быть неясно почему она так сделала, и когда, при каких условиях с ней это происходит • А также, рассредоточиваем логику – Причины произошедшего с кнопкой могут быть разбросаны по разным местам исходного кода приложения – Тем более, со временем
  • 10.
    Традиционный подход: проблемы •Incidental complexity – В результате мы пишем код, несущественный для исходной задачи (домена) – Тратим ресурсы на то чтобы решить когда – А также, на то чтобы разобраться в решениях коллег • Трудности автоматизированного тестирования – На основе робота
  • 11.
    Есть другой способ •Библиотека берёт часть рутины на себя – Однажды реализована и оттестирована – Стремимся к тому, чтобы разработчик концентрировался на своей задаче (домене)
  • 12.
    Есть другой способ •Библиотека берёт часть рутины на себя – Однажды реализована и оттестирована – Стремимся к тому, чтобы разработчик концентрировался на своей задаче (домене) • Вся логика одного свойства в одном месте
  • 13.
    Есть другой способ •Библиотека берёт часть рутины на себя – Однажды реализована и оттестирована – Стремимся к тому, чтобы разработчик концентрировался на своей задаче (домене) • Вся логика одного свойства в одном месте • Обычные юнит-тесты для кода, который управляет моделью и состоянием виджета
  • 14.
    Hello world withFlatGUI (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}})
  • 15.
    Hello world withFlatGUI (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}})
  • 16.
    Hello world withFlatGUI (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}})
  • 17.
    Чем это отличается? •greeting-evolver является единым место для всей логики свойства :text лейбла
  • 18.
    Чем это отличается? •greeting-evolver является единым местом для всей логики свойства :text лейбла • Эта функция содержит логику, не рутину
  • 19.
    Чем это отличается? •greeting-evolver является единым место для всей логики свойства :text лейбла • Эта функция содержит логику, не рутину • Эту функцию легко отлаживать
  • 20.
    Чем это отличается? •greeting-evolver является единым место для всей логики свойства :text лейбла • Эта функция содержит логику, не рутину • Эту функцию легко отлаживать • Эту функцию легко покрыть юнит-тестами
  • 21.
    Чем это отличается? •greeting-evolver является единым место для всей логики свойства :text лейбла • Эта функция содержит логику, не рутину • Эту функцию легко отлаживать • Эту функцию легко покрыть юнит-тестами • Разработчику не нужно решать когда её вызвать
  • 22.
    Чем это отличается? •greeting-evolver является единым место для всей логики свойства :text лейбла • Эта функция содержит логику, не рутину • Эту функцию легко отлаживать • Эту функцию легко покрыть юнит-тестами • Разработчику не нужно решать когда её вызвать • Разработчику не нужно её вызывать
  • 23.
    Чем это отличается? •Реактивный движок берёт эти заботы на себя – С каждым новым GUI контейнером, как бы ни отличалась его предметная область, по сути мы решаем одни и те же задачи – Однажды реализованный движок прячет их под капот – А также, покрывает тестами
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
    Опыт использования Clojure •Макросы здесь очень кстати – Создание типов и объектов виджетов, property evolver -функций – Layout management: “ниже” “справа” и т.п. – Возможностей IDE ещё немного недостаточно • Было много профилирования – Появилось немного не-идиоматического Clojure кода
  • 29.
    Что дальше? • Focus-management– первое, чего не хватает, и что скоро появится • Оптимизации для сетевого варианта • Писать тесты и документацию • Доводить имеющиеся виджеты и делать новые • Пожелания приветствуются • Участие приветствуется
  • 30.