Подтвердить что ты не робот

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

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

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

спасибо, -Morgan

4b9b3361

Ответ 1

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

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

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

Ответ 2

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

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

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

в

Foo.DoSomething();  

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

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

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

-PC

Ответ 3

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

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

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

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

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

Обновление: здесь приведен пример с 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 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()

Ответ 4

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

Constructing the User Interface with Statecharts
by Ian Horrocks, Addison-Wesley, 1998

Ответ 5

Это не проблема UI, если честно.

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

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

Ответ 6

Я получил прези-презентацию о шаблоне, который я называю "State First".

Это комбинация 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, вы получаете über-низкую связь между видами/моделями, высокой степенью вероятности и (если ваш язык позволяет) очень безопасно.

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

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

Ответ 7

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

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

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

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

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

Ответ 8

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

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

Ответ 9

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

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

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

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

Наличие обобщенного набора состояний может определенно помочь устранить ваш код!

Ответ 10

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

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

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

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

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

-Adam