Государственные машины и пользовательский интерфейс работают какие-нибудь примеры / опыт?

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

Итак, вопрос в том, думает ли кто-нибудь из вас, программистов пользовательского интерфейса, в своей работе терминами государственных машин? Если да, то как?

спасибо, -Morgan


person morgancodes    schedule 27.02.2009    source источник
comment
Это не я, но в последнее время люди здесь, кажется, думают, что все вопросы о StackOverflow имеют значение, подобное Википедии. Так что, если вы допустили опечатку или допустили ошибку, они берут на себя ответственность исправить это за вас. Очень полезно, да? Да...   -  person Randolpho    schedule 27.02.2009
comment
Джефф Этвуд настоятельно не рекомендует использовать подписи, приветствия и приветствия, поэтому некоторые модераторы взяли эстафету и удалили такую ​​лишнюю информацию.   -  person Adam Davis    schedule 27.02.2009
comment
@Randolpho: Это часть дизайна этого места. Лично я считаю, что уместно оставить и хорошую заметку для редактирования, и комментарий, но это я ...   -  person dmckee --- ex-moderator kitten    schedule 27.02.2009
comment
Здесь: stackoverflow.com/questions/468303/   -  person Adam Davis    schedule 27.02.2009


Ответы (10)


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

Основное преимущество заключается в том, что он позволяет мыслить на более высоком уровне абстракции с более высокой степенью детализации. Вместо того чтобы думать: «Если кнопка A нажата, то поле со списком B заблокировано, текстовое поле C очищено, а кнопка D разблокирована», вы думаете: «Нажатие кнопки A переводит приложение в состояние ПРОВЕРИЛ» - и вход в это состояние означает, что определенные вещи случаться.

Однако я не думаю, что полезно (или даже возможно) моделировать весь пользовательский интерфейс как единый конечный автомат. Вместо этого обычно существует ряд меньших конечных автоматов, каждый из которых обрабатывает одну часть пользовательского интерфейса (состоящий из нескольких элементов управления, которые взаимодействуют и концептуально принадлежат друг другу), и один (может быть, более одного) «глобальный» конечный автомат, который обрабатывает более фундаментальные проблемы.

person Michael Borgwardt    schedule 27.02.2009
comment
Отличный ответ. Спасибо, Майкл. - person morgancodes; 27.02.2009
comment
@Michael Borgwardt Скажите, пожалуйста, о каком фреймворке вы писали. Большое спасибо. - person Ilan; 13.05.2014
comment
@Ilan: как я уже писал, он был проприетарным, создан банком для внутреннего использования. Это не с открытым исходным кодом, и я не думаю, что вы можете его купить. - person Michael Borgwardt; 13.05.2014
comment
@ Майкл Боргвардт А, ладно. Вы когда-нибудь знакомы с фреймворком с открытым исходным кодом или коммерческим? Спасибо! - person Ilan; 14.05.2014
comment
@Ilan: эту концепцию легко реализовать в любом фреймворке, который реализует достаточно мощную привязку данных (одна из них - Knockout: knockoutjs.com/documentation/computedObservables.html). Идея состоит в том, что у вас есть состояние свойства модели, которое зависит от набора других свойств (т.е. вычисляется из него), и когда оно изменяется, другой набор свойств изменяется в зависимости от нового состояния. - person Michael Borgwardt; 14.05.2014

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

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

Вместо того, чтобы мои приложения находились в различных состояниях, представленных флагами состояния и режимами, я использую продолжения, чтобы контролировать, что приложение будет делать дальше. Проще всего объяснить на примере. Предположим, вы хотите открыть диалоговое окно подтверждения перед отправкой электронного письма. Шаг 1 создает электронное письмо. Шаг 2 получает подтверждение. Шаг 3 отправляет электронное письмо. Большинство наборов инструментов пользовательского интерфейса требует, чтобы вы передавали управление обратно в цикл событий после каждого шага, что делает это действительно уродливым, если вы пытаетесь представить его с помощью конечного автомата. С продолжениями вы не думаете о шагах, которые навязывает вам инструментарий - это всего лишь один процесс создания и отправки электронного письма. Однако, когда процессу требуется подтверждение, вы фиксируете состояние своего приложения в продолжении и передаете это продолжение кнопке ОК в диалоговом окне подтверждения. При нажатии ОК ваше приложение продолжает работу с того места, где оно было.

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

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

Обновление: вот полный пример Qt на Ruby. Интересные части находятся в ConfirmationButton и MailButton. Я не эксперт по Qt или Ruby, поэтому буду признателен за любые улучшения, которые вы все можете предложить.

require 'Qt4'

class ConfirmationWindow < Qt::Widget
  def initialize(question, to_do_next)
    super()

    label = Qt::Label.new(question)
    ok = ConfirmationButton.new("OK")
    ok.to_do_next = to_do_next
    cancel = Qt::PushButton.new("Cancel")

    Qt::Object::connect(ok, SIGNAL('clicked()'), ok, SLOT('confirmAction()'))
    Qt::Object::connect(ok, SIGNAL('clicked()'), self, SLOT('close()'))
    Qt::Object::connect(cancel, SIGNAL('clicked()'), self, SLOT('close()'))

    box = Qt::HBoxLayout.new()
    box.addWidget(label)
    box.addWidget(ok)
    box.addWidget(cancel)

    setLayout(box)
  end
end

class ConfirmationButton < Qt::PushButton
  slots 'confirmAction()'
  attr_accessor :to_do_next
  def confirmAction()
    @to_do_next.call()
  end
end

class MailButton < Qt::PushButton
  slots 'sendMail()'
  def sendMail()
    lucky = rand().to_s()
    message = "hello world. here's your lucky number: " + lucky
    do_next = lambda {
      # Everything in this block will be delayed until the
      # the confirmation button is clicked. All the local
      # variables calculated earlier in this method will retain
      # their values.
      print "sending mail: " + message + "\n"
    }
    popup = ConfirmationWindow.new("Really send " + lucky + "?", do_next)
    popup.show()
  end
end

app = Qt::Application.new(ARGV)

window = Qt::Widget.new()
send_mail = MailButton.new("Send Mail")
quit = Qt::PushButton.new("Quit")

Qt::Object::connect(send_mail, SIGNAL('clicked()'), send_mail, SLOT('sendMail()'))
Qt::Object::connect(quit, SIGNAL('clicked()'), app, SLOT('quit()'))

box = Qt::VBoxLayout.new(window)
box.addWidget(send_mail)
box.addWidget(quit)

window.setLayout(box)
window.show()
app.exec()
person Ken Fox    schedule 27.02.2009
comment
Очень интересно. Я обязательно последую твоей рекомендации Google. - person morgancodes; 27.02.2009

Это не пользовательский интерфейс, который нужно моделировать как конечный автомат; это отображаемые объекты, которые может быть полезно смоделировать как конечные автоматы. Затем ваш пользовательский интерфейс становится (упрощение) набором обработчиков событий для изменения состояния различных объектов.

Это изменение от:

DoSomethingToTheFooObject();  
UpdateDisplay1();  // which is the main display for the Foo object  
UpdateDisplay2();  // which has a label showing the Foo's width,
                   // which may have changed  
...  

to:

Foo.DoSomething();  

void OnFooWidthChanged() { UpdateDisplay2(); }  
void OnFooPaletteChanged() { UpdateDisplay1(); }  

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

Если вы обнаружите, что из 100 элементов пользовательского интерфейса, которые, возможно, потребуется перерисовать при изменении состояния Foo, все они должны быть перерисованы при изменении палитры, но только 10 при изменении ширины, это может предложить что-то о том, какие события / состояние Изменения Foo должны сигнализировать. Если вы обнаружите, что у вас есть большой обработчик событий OnFooStateChanged (), который проверяет ряд свойств Foo, чтобы увидеть, что изменилось, в попытке минимизировать обновления пользовательского интерфейса, он предлагает кое-что о гранулярности модели событий Foo. Если вы обнаружите, что хотите написать небольшой автономный виджет пользовательского интерфейса, который можно использовать в нескольких местах пользовательского интерфейса, но он должен знать, когда изменяется Foo, и вы не хотите включать весь код, который реализация Foo несет с собой, это предлагает кое-что об организации ваших данных относительно вашего пользовательского интерфейса, где вы используете классы по сравнению с интерфейсами и т. д. По сути, это заставляет вас более серьезно задуматься о том, что такое ваш уровень представления, более серьезно чем "весь код в моих классах формы".

-PC

person user18329    schedule 27.02.2009
comment
Когда-то мы называли это MVC. Это было до того, как Rails уничтожил этот термин. - person Frank Krueger; 27.02.2009

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

Constructing the User Interface with Statecharts
by Ian Horrocks, Addison-Wesley, 1998
person Dill    schedule 01.07.2009
comment
Книга находится на Scribd - person maverick; 15.04.2015
comment
Загрузка Scribd только что взята с bookzz.org (у которого нет регистратора). - person dcow; 18.01.2017

Мы только что говорили о Конструировании пользовательского интерфейса с помощью диаграмм состояний Хоррокса, цены на которые второстепенные составляют от 250 до почти 700 долларов. Наш менеджер по разработке программного обеспечения оценивает ее как одну из самых важных книг, которые у него есть (к сожалению, он живет на другом конце света).

Книги Самека о диаграммах состояний во многом основаны на этой работе, хотя и в несколько иной области и, как сообщается, не столь ясны. "Практические диаграммы состояний UML в программировании на C / C ++ на основе событий для Встроенные системы "также доступны в Safari.

Хоррокса цитируют довольно часто - на портале ACM есть двадцать статей, так что если вы там есть доступ, вы можете найти что-нибудь полезное.

Есть книга и программное обеспечение FlashMX для интерактивного моделирования. У них есть образец главы в формате PDF с диаграммами состояний.

Объекты, компоненты и платформы с UML: подход катализа (SM) есть глава о моделях поведения, которая включает около десяти страниц полезных примеров использования диаграмм состояний (я отмечаю, что они доступны очень дешево из вторых рук). Он довольно формальный и сложный, но этот раздел легко читать.

person Andy Dent    schedule 20.07.2011
comment
Книга Хоррока опубликована на Scribd - person maverick; 15.04.2015

Честно говоря, это не проблема пользовательского интерфейса.

Я бы сделал следующее:

  1. Определите свои состояния
  2. Определите свои переходы - какие состояния доступны из каких других?
  3. Как запускаются эти переходы? Какие события?
  4. Напишите свой конечный автомат - сохраните текущее состояние, получите события, и если это событие может вызвать допустимый переход из текущего состояния, измените состояние соответствующим образом.
person PaulJWilliams    schedule 27.02.2009
comment
Спасибо. Вопрос не в том, что такое государственная машина или что бы вы сделали, а в том, что вы сделали. - person morgancodes; 27.02.2009
comment
@ [morgancodes]: возможно, вам нужно перефразировать свой вопрос вместо попыток голосования против, чтобы быть полезным - person Steven A. Lowe; 27.02.2009
comment
Верно. Я предположил, что это то, что я делал несколько раз. Почему бы мне не последовать собственному совету? - person PaulJWilliams; 27.02.2009
comment
@Visage Извините, я неправильно понял и подумал, что вы говорите гипотетически, а не исходя из опыта. Я удержал палец на спусковом крючке при голосовании против. - person morgancodes; 27.02.2009

Я получил прези-презентацию паттерна, который я назвал «Государство прежде всего».

Это комбинация MPV / IoC / FSM, и я успешно использовал ее в .Net / WinForms, .Net / Silverlight и Flex (на данный момент).

Вы начинаете с написания кода вашего FSM:

class FSM
    IViewFactory ViewFactory;
    IModelFactory ModelFactory;
    Container Container; // e.g. a StackPanel in SL
    ctor((viewFactory,modelFactory,container) {
        ...assignments...
        start();
    }

    start() {
        var view = ViewFactory.Start();
        var model = ModelFactory.Start();
        view.Context = model;
        view.Login += (s,e) => {
            var loginResult = model.TryLogin(); // vm contains username/password now
            if(loginResult.Error) {
                // show error?
            } else {
                loggedIn(loginResult.UserModel); // jump to loggedIn-state
            }
        };
        show(view);
    }


    loggedIn(UserModel model) {
        var view = ViewFactory.LoggedIn();
        view.Context = model;
        view.Logout += (s,e) => {
            start(); // jump to start
        };
        show(view);
    }

Затем вы создаете свои IViewFactory и IModelFactory (ваш FSM позволяет легко увидеть, что вам нужно)

public interface IViewFactory {
    IStartView Start();
    ILoggedInView LoggedIn();
}

public interface IModelFactory {
    IStartModel Start();
}

Теперь все, что вам нужно сделать, это реализовать IViewFactory, IModelFactory, IStartView, ILoggedInView и модели. Преимущество здесь в том, что вы можете видеть все переходы в FSM, вы получаете сверхнизкую связь между представлениями / моделями, высокую тестируемость и (если ваш язык позволяет) большое количество безопасных типов.

Одним из важных моментов при использовании конечного автомата является то, что вы не должны просто прыгать между состояниями - вы также должны переносить все данные с сохранением состояния при переходе (в качестве аргументов, см. loggedIn выше). Это поможет вам избежать глобальных состояний, которые обычно засоряют графический код.

Вы можете посмотреть презентацию по адресу http://prezi.com/bqcr5nhcdhqu/, но она не содержит примеров кода по адресу момент.

person finnsson    schedule 01.10.2009
comment
Очень интересная идея! - person kol; 25.11.2017

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

Это сопоставление позволит вам видеть неиспользуемые состояния или состояния, в которых несколько кнопок или путей могут вести к одному и тому же состоянию и никакие другие (те, которые можно комбинировать).

person Jeremy L    schedule 27.02.2009
comment
Спасибо. Вопрос не в том, что такое государственная машина или что бы вы сделали, а в том, что вы сделали. - person morgancodes; 27.02.2009
comment
@ [morgancodes]: попытки проголосовать против вас не побуждают других помогать вам. - person Steven A. Lowe; 27.02.2009
comment
@ [Стив А. Лоу] Не хотел расстраивать, просто хотел прояснить вопрос. В каких ситуациях голос "против" считается правильным? - person morgancodes; 27.02.2009
comment
Голосование против - правильный шаг, когда ответ не по теме или неуместен. Это означало, что его нужно использовать экономно. Вместо этого следует прокомментировать ответы, требующие доработки. - person Jon W; 27.02.2009

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

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

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

При обнаружении определенных событий состояния кнопок / экранных объектов автоматически меняются.

Обобщенный набор состояний определенно может помочь избавиться от спагеттификации вашего кода!

person Praveen Sharma    schedule 27.02.2009
comment
Спасибо Правин. Вы еще не видели результатов? Облегчило ли это вашу жизнь или еще рано говорить об этом? - person morgancodes; 27.02.2009
comment
Да! Это сделало мою жизнь проще. Использование STATES для элементов пользовательского интерфейса во фреймворке сократило почти 50–100 строк кода на моем конце для каждого класса шаблона. - person Praveen Sharma; 03.04.2009

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

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

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

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

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

-Адам

person Adam Davis    schedule 27.02.2009