Update. Дописанный примерчик по теме.
Каждый флэш разработчик сталкивается в своей жизни с понятием СОСТОЯНИЯ объекта. Часто до какого-то момента человек не осознает, что он снова и снова придумывает себе то, чем наука уже давно пользуется — конечный автомат. Порой, это не так очевидно. Но, мне уже долгое время такой подход к проектированию помогает не тратить время на непонятные баги.
Представим простой пример. Есть флэшовое меню. Кто из нас не делал флэшовых меню, правда? У него (в зависимости от сложности), есть состояния, например: закрыто, анимация, открыто. Вы не представляете СКОЛЬКО я видел (да что тут преукрашать, и сам делал) менюшек, которые глючили из-за того, что что-то происходило в ненужном состоянии. Если их всего три, то, скажем реакцию на нажатия во время анимации можно легко отключить, чтобы ничего не ломалось. А что если у меня ветвящаяся цепочка изменения множества визуальных элементов, которая может быть прервана и выполнена в обратном направлении в наборе ключевых точек? Вот тут начинается жопа с отладкой, если в самом начале не принять мер.
Я помню свой пример десятикратно вложенных IF-ов с кучей переменных и граничных условий. Все это легко заменялось набором состояний с переходами.
Давно уже я себе слепил простую библиотечку для работы с состояниями, переходами и событиями. Неделю назад я на нее посмотрел трезвым взглядом и выкинул 90% всяких ненужных вещей и левого функционала. Осталась простенькая штуковинка, которая уже неделю успешно применяется вместо старого монстра. Фиксятся косячки, но тем не менее.
Идея — простая переформулировка вышесказанного. Есть состояния, возможные переходы между состояниями (в том числе и сложные) и события. Соответственно, чтобы указать, что мне нужно это событие в этом состоянии, я навешиваю на него листенер. Также навешиваются листенеры на сами переходы из состояния A в состояние B.
Пока писал, придумал BUG. При составном переходе из A в B через C будет евент перехода из A в C и из C в B, а не ожидаемый A в B. Записал пофиксить на выходных.
Как дотестирую еще, выложу на какой-нить гуглокод. Пока, выдержка из readme.
/**
* РУКОВОДСТВО.
* 0. Все состояния имеют строковые имена. Допустим, что
* const INVISIBLE, VISIBLE, SHOW содержат названия состояний.
* 1. Добавить состояния
* // Просто добавляет состояние без перехода
* state.add( INVISIBLE );
* // Добавляет состояние INVISIBLE и переход из него в VISIBLE
* state.add( INVISIBLE, VISIBLE );
* // Добавляет состояние INVISIBLE и переход из него в VISIBLE через SHOW
* state.add( INVISIBLE, VISIBLE, SHOW );
* Когда добавляется самое первое состояние, оно принимается за текущее. При этом не срабатывают события смены состояния.
* Второй параметр функции может быть массивом состояний.
* 2. Определить листенеры смены состояний
* // Выполняет stateVisible, когда состояние из любого меняется на VISIBLE
* state.addTransitionListener( StateMachine.ANY, VISIBLE, stateVisible );
* // Выполняет stateShow, когда состояние меняется с INVISIBLE на SHOW
* state.addTransitionListener( INVISIBLE, SHOW, stateShow );
* 3. Определить листенеры событий
* // Вызывает doShow, когда событие “show” диспатчится в любом состоянии
* state.addEventListener( StateMachine.ANY, “show”, doShow );
* // Вызывает doHide, когда событие “hide” диспатчится в состоянии VISIBLE
* state.addEventListener( VISIBLE, “hide”, doHide );
* Листенеры событий являются просто колбэк функциями, и не имеют отношение к флэшвой событийной системе и классу Event
* 4. Поустанавливать состояния и посмотреть работают ли листенеры изменений
* state.setState( VISIBLE );
* 5. Подиспатчить события и посмотреть работают ли листенеры события
* state.dispatch( “hide” );
*
* ПОСЛОЖНЕЕ.
* 0. Если состояние из текущего в требуемое не может быть изменени, то оно просто не изменяется. Никаких ошибок не выбрасывается.
* 1. Если нужно добавть двусторонние транзишены, например из состояния A в B и из B в A, используйте метод addTwoWay().
* addTwoWay( A, B, via: * = null);
* 2. Чтобы передать параметры листенерам событий, используйте второй параметр.
* Можно передать как один аргумент так и массив аргументов.
* dispatch( name, args: * = null )
* 3. Можно удалить листенер перехода или события.
* removeTransitionListener() and removeEventListener();
* 4. Цепочки состояний.
* Можно создавать сложные переходы через виртуальные и реальные состояния. Виртуальное состояние — это состояние,
* которое не было зарегистрировано методом add() и не имеет переходов из/в себя. Чтобы использовать цепочки,
* нужно указать промежуточные состояния в третьем параметры метода state.add(). Это может быть единичное состояние
* или массив состояний.
* state.add( INVISIBLE, VISIBLE, SHOW ) создаст такой переход:
* INVISIBLE –> SHOW –> VISIBLE. При переходе на внутренний состояния также срабатывают все листенеры. Здесь
* SHOW — виртуальное состояние. Эта конструкция считается одним переходом из INVISIBLE в VISIBLE, но как видите
* является асинхронным переходом. Состояние SHOW может выполняться какое-то время что-то рисуя на экране,
* когда оно заканчивается, нужно вызвать метод state.release(), иначе конечный автомат никогда не выйдет из
* этого состояния.
* Выполнение цепочки может быть остановлено только в реальных состояниях и если canChangeStateDuringTransition == true.
* Это случается, если смена состояния запрашивается во время перехода. Новое состояние встает в очередь и рассматривается
* после завершения перехода.
*/
И временная ссылочка.






