singlepost

Конечный автомат (state machine) со вложенными состояниями << На главную или назад  

Кто-нибудь имеел удовольствие делать такую штуку? Мне нужно для GUI, хочу услышать good & bad design decisions от уже наступавших на грабли.

Если кому интересно, расскажу подробнее. Может, сам лучше разберусь.

88 ответов в теме “Конечный автомат (state machine) со вложенными состояниями”

  1. 13
    Олег Андреев ответил:

    У меня все состояния от класса State, подклассов нет. Функции входа и выхода я задаю анонимными функциями.

    Про файловую систему ничего посоветовать не могу.

  2. 12
    Владимир Веревкин ответил:

    Что упростить? Механизм. Ты видимо каждое состояние создаёшь как отдельный класс, но в большинстве случаев, думаю можно обойтись одним классом "состояние", для которого написан соответствующий интерфейс (имя, добавление/удаление специфических свойств, добавление/удаление определённых методов/событий, возможно чтото ещё). У меня как раз сейчас основной проект посвящён графам… Кстати, где почитать про то, как грамотно спроектировать файловую систему (на физическом уровне, какие должны быть структуры)? Желательно чтобы кратко и по существу. Больше всего интересует вопрос, как свести фрагментацию к минимуму.

  3. 11
    Олег Андреев ответил:

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

    Мне понадобилось сделать так только один раз: для состояния "объект подсвечен". Для состояния deselected я сделал ребенка hilited и при входе/выходе из хайлайтед довольно хитрую анимацию с задержкой и колбеком после анимации. Получил кучу глюков и, в итоге, сделал очевидное разделение: у deselected двое детей: shadowed, hilited. И вместо deselected в таблице переходов прописал shadowed. Общие ресурс (обработка onClick) для этих состояний остался в deselected.

  4. 10
    Олег Андреев ответил:

    Еще новости с фронта. Есть такие состояния, которые условно назовем параметрическими. Есть набор превьюх, между которыми можно выбирать, что играть. У плейлиста есть состояния "играет", "не играет" и их родительское состояние – "выбрано" (где ставится прослушивание событий выбранного объекта). Одна из превьюх всегда выбрана.
    Суть в том, что когда выбирается другая превьюха и нужно перейти, скажем, в состояние "не играет", нужно выйти из родительского "выбрано" и войтив него снова, дойдя до "не играет". Хотя базовый механизм использует наикратчайший путь: от "играет" до "не играет" без выхода из "выбрано".

    Решить проблему можно двумя способами (правильным и неправильным :-)

    1) Ввести механизм параметрических состояний. В матрице переходов нужно указать параметры для состояний (причем lazy eval, в не какие-то фиксированные) и составить логику выхода из родителей, пока параметр не тот, который нужен. Это не так-то просто.

    2) Ввести редирект. Создать дополнительное состояние на нужном уровне вложенности, которое в момент входа делает переход в другое состояние.Так можно будет перейти из А в Б более длинным путем, с выходом из их родителя.

    Синтаксис в этом случае вообще рулит (AS3): в матрице нужно указать прокси-состояние, но его не нужно заранее содавать. Нужно его создать при необходимости:

    base.to(child)

    в строке матрицы (см. ссылки выше для пояснения):

    [playlist, Playlist.SELECT_ITEM,null,stopped,loaded.to(stopped),loaded.to(playing) ]

    Ну, и чудо-сорцы (class State):

    public function to(dest:State)
    {
    return new State(this, { enter:function(…args){ dest.enter.apply(dest, args) } });
    }

  5. 9
    Олег Андреев ответил:

    Да, верно. "Освобождение ресурсов происходит до первого общего родителя". Это нужно для расшаривания ресурсов между состояниями.

    На графе в силу моей лени состояния-родители не показаны. Их можно изобразить, объединив светлой линией дочерние состояния. В представленном графе перехода к родителю нет, зато есть в новой версии – под deselected добавлено состояние highlighted. Соответственно, переход в него происходит по mouse_over с включением подсветки. Ну и выход соответствующий.

    Исходное – deselected (а какое это имеет значение?)

    "Видеосервер" – класс, реализующий доступ к скачиванию видеофайла с сервера. V.stop – это событие, выкидываемое "видеосервером" когда стриминг прекратился. V.full – сообщение о заполнении буфера на 100%. И т.д.

  6. 8
    Олег Андреев ответил:

    Проблема, собственно, в том, как этот КА описать (что уже придумано совместно с Костей Олениным (id499348) — смотри картинки) и в том, каких принципов придерживаться при работе с ним. Мне пока рано что-то рассказывать – штука еще не доделана, все может поменяться.

    А что бы ты упростил?

  7. 7
    Владимир Веревкин ответил:

    я правильно понял?

    существует иерархия состояний и в случае если состояние перетекает из одного в другое, освобождение ресурсов происходит до первого общего родителя?

    на графе непонятно:
    – какое состояние от какого наследуется
    – какое состояние исходное
    – что такое видеосервер?

  8. 6
    Владимир Веревкин ответил:

    клёвая идея :) надо будет взять на заметку, чуть упростить, и реализовать в своих проектах. а в чём, собственно, проблема?

  9. 5
    Олег Андреев ответил:

    Полный граф все-тки пришлось нарисовать. Иначе вообще ничего не понятно.

    Картинка: //vkontakte.ru/photos.php?act=show&id=28533530
    Код: //vkontakte.ru/photos.php?act=show&id=28533533

  10. 4
    Олег Андреев ответил:

    "Множество входов не раздувается", т.к. переходы из А в Б разбиты на три части, каждая из которых используется повторно: выход, событие перехода, вход. Выход и вход, понятное дело, используются повторно при любых переходах. А вот событие перехода может работать, как для конкретной пары А-Б, так и для группы пар: А-Б, А-В, Б-В, например. Но это уже относится к ТЗ, которое считаем фиксированным.

  11. 3
    Олег Андреев ответил:

    Нужно для непосредственного кодирования — чтобы не сломать голову. Есть MVC-приложение: M – модели, которые хранят бизнес-логику, V – views, графические интерактивные куски (считай, "компоненты"), C (controllers) — это механизм подписки и рассылки событий. Язык — ActionScript 3 (Flash). Работает так: виды подписываются на события от моделей и о самих себя, включая внутренние виды (например, ThumbnailList содержит десяток Thumbnail-ов). Такие события приводят к переходу из состояния в состояние. Сами состояния описываются как объекты с парой функций и указателем на состояние-родителя ("контейнера"). Пара функций – это вход и выход из состояния. В этих функциях происходит выделение и освобождение ресурсов соответственно. Такими действиями с ресурсами, в первую очередь, являются addListener и removeListener. Во вторую очередь, это включение-выключение других компонентов (кнопок всяких).

    Таким образом, переход из состояния А в состояние Б – это отлов события, вызов выхода из А и всех его предков вплоть до общего предка А и Б, затем вход во всех предков Б вплоть до Б.

    Вот, например, иерархия состояний (тире для отступов):

    — loadingState
    — loadedState
    — — browsingState
    — — — selectedState
    — — watchingState

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

    При переходе из selectedState в watchingState, выход из loadedState не производится (общий предок). Т.е. это общее состояние характеризует общие для дочерних состояний ресурсы.

  12. 2
    Константин Романов ответил:

    Мы на это смотрели, но не с точки зрения UI, а с точки зрения реализации торговых стратегий, поэтому мы смотрели на готовые решения (а есть некоторый набор форматов и гуевых редакторов для разных FSM).

    А в чем польза этого для GUI? Overview, документирование и тестирование? Если да, то для простых гуев это наверное overkill, а для сложных – скорее всего еще добавит сложности. Например, если у вас куча контролов (а не несколько как в типичных примерах про CD-player), то множество входов раздувается, и, если интерфейс не сильно модальный, их придется обрабатывать почти в каждом состоянии. Поэтому IMHO граф – плохая абстракция здесь. Вот если граф + что-то (есть набор входов, которые обрабатываются поичти всегда одинаково вне зависимости от состояния) – это, наверное, интереснее.

  13. 1
    Константин Романов ответил:

    У этих вот вроде есть что-то типа отдельной обработки стандартных входов: //smc.sourceforge.net/

Клуб программистов работает уже ой-ой-ой сколько, а если поточнее, то с 2007 года.