Механизм конвейера
Каждый из разработчиков приложений во Flash в конечном итоге сталкивается с задачей выполнения команд в нужной последовательности. По большому счету работа приложения есть последовательность действий. Приложение выполняет код строчка за строчкой. Используя блоки управления (циклы и условия) мы направляем работу приложения в нужное нам русло. Все это так привычно и очевидно, что мы принимаем это как должное.
Все ли так хорошо? Для аниматора и дизайнера все хорошо. Кода мало, Flash все играет, код выполняется и делает то, что нужно. Проблемы начинают возникать, когда скрипт превращается в приложение, и появляется необходимость в средствах управления работы совсем другого уровня.
В этой главе предлагается мощное и проверенное решение, которое как значительно упростит и упорядочит работу Flash-программиста.
Итак, почему плохо.
Если обратится к другим языкам и средам исполнения, то становится очевидным, что мы значительно обделены в средствах управления работой наших приложений.
Во-первых, наши приложения имеют только один поток исполнения. Многим, кто разрабатывал приложения только на Flash, и не имеющим опыты разработки приложений, например, на Java , и в голову не может придти мысль, что может быть как-то иначе. В Java же, потоки можно создавать, останавливать на время, и удалять. Возможность управления потоками исполнения (threads) значительно упрощает жизнь разработчику. Мы такой возможности не имеем.
Во-вторых, поток исполнения во Flash зависит от анимационных фреймов. Код не может выполняться слишком долго, ведь новый фрейм не будет проигран (плеер не войдет в следующий фрейм) пока не будет выполнен весь код, исполняемый в предыдущем фрейме. В противном случае плеер выдаст предупреждении об излишней задержке (более чем пятнадцать секунд). Это ограничение заставляет нас «распределять» выполнение кода приложения так, чтобы он не влиял на процесс проигрывания. К примеру, fps в документа равен 24, следовательно, каждый фрейм должен проигрываться чуть больше чем 40 миллисекунд. Код должен быть достаточно быстрым и равномерно распределенным между фреймами, чтобы не мешать процессу вывода графики на экран. Задача не из легких. И «не-флэшеру» покажется даже слегка маразматичной.
Значит, нам нужен механизм, который предоставит сервис для управления потоком управления и позволит распределить нагрузку (исполнение кода) равномерно во времени (между фреймами).
Уточним само понятие «поток исполнения», сделаем ее более приближенной к нашим повседневным задачам. Нужно ли нам управлять работой каждой строчки кода? Очевидно, что нет. Строчка как элемент потока исполнения по большому счету нам не важна. Важно действие, которое будет выполнено. А действием может быть и группа строк, и некий метод объекта. Другим словом, нам важна некая команда. Следовательно, термин «поток исполнения» трансформируется в термин «поток выполнения команд». Почему это важно? Важно потому, что новый взгляд на задачу избавляет нас от трудоемкой и по большему счету вредной идеи создать надстройку в виде интерпретатора (и это уже другая история).
Если пошел разговор о командах, то сразу приходит на ум шаблон проектирования «Command». Данный паттерн предлагает решение, в котором для каждой команды мы создаем отдельный объект, которых имеет специальный метод, хранящий код команды. Имея список таких объектов, вызываем метод исполнения (execute) и выполняем команду за командой. Все бы хорошо, но данный подход для нас слишком громоздкий. Во-первых, нужно делать на каждую команду свой класс, что трудоемко. Во-вторых, следуя «букве паттерна» мы значительно ограничиваем себя в маневре, и это станет ясно чуть позже. Забегая чуть вперед, отмечу, что ничего нам не мешает использовать данный паттерн в нашем механизме. Просто у нас будет возможность выбора.
Суммируя сказанное в предыдущем абзаце, делаем вывод, что наш механизм должен быть более «низкоуровневым», должен позволять нам, просто вызвать методы любых объектов, и изменять их поля. Это нам позволит не ограничивать себя каким-то конкретным шаблоном или шаблонами.
Теперь хотелось бы обратиться к истории создания этого механизма. Когда этот механизм зарождался, я не знал о паттернах проектирования, и о существовании ООП. Чтобы постичь мощь этих знаний, нужно было пройти пару версий Flash, и пару раз сделать генеральную уборку в своей голове.
Итак, на дворе четвертый Flash, и «приложения» делают так. Каждое действие приложение помещается в отдельный мувиклип, без графики, который имеет набор неких фреймов. Фреймы реализуют логику одной протяженной во времени «команды». Запуская проигрывание некого мувиклипа-команды, мы инициализировали процесс выполнения этой команды. Фреймы проигрываются, код в них выполняется, и в конечном итоге очередь доходит до последнего фрейма в мувиклипе, который «рапортует» следующему мувику-команде, начать свою работу. Эта простая система естественным образом эволюционировала, сначала пришла мысль, что внутри этих клипов легко создавать циклы в виде переходов между кадрами и условий, и как следствие повторение выполнения каких-то отдельных действий. Далее пришла идея, что связь между текущий-следующий может быть динамической, и какую команду выполнять следующей зависит от условий в приложении. Отсюда список. Еще идея, действия в кадрах, часто повторяются в разных мувиках, и стоит вынести код из клипов «наверх», поместить их в кадрах рута. И так далее. Это была уже система, точнее стратегия разработки приложений, с некоторой точки зрения даже совершенная стратегия.
Условно такую стратегию можно назвать «вагончиками».
Новая версия Flash, заставляет пересмотреть свои подходы к разработке, и что до этого казалось почти совершенным – кажется уже просто убогим. Во-первых, появляются массивы, и работа со списками становится куда более удобной. Появляются обработчики событий входа в кадр. Появились функции. Да и вообще, изменяется весь ActionScript!
Вот в это революционное время и родилась идея конвейера. Если вместо физических «вагончиков» хранить команды в массиве, где команда – это функция. При входе в кадр вызывается следующая функция. Если нужна пауза между командами – вставляем пустые элементы. При всей внешней простоте и очевидности сейчас, тогда эта идея была для меня откровением. Оставалось применить эту стратегию в своей работе.
Конвейер состоял из одного мувиклипа, который вызывал одну и ту же функцию при входе в кадр. Список хранился в одном массиве. Для помещения команды в массив конвейера была создана еще одна функция. Наделив все свои новые разработки этим механизмом, и переписав старые приложения, начали появляться новые идеи, и механизм начал развиваться.
Во-первых, время пауз между командами можно задавать не только во фреймах, время иногда удобнее задавать в миллисекундах. Во-вторых, работу конвейера иногда нужно временно остановить. Например, дождаться данных с сервера. И когда нужно возобновить его работу. В-третьих, если массива длиной в несколько сотен строк, то работа конвейера становилась очень медленной. Нужна была идея для решения проблемы длинного списка команд. В-четвертых, практика показала, что конвейер очень удобен для создания анимации движения из точки в точку, появилась потребность встроенной в механизм конвейера функции построения анимации из точки в точку. И, наконец, в-пятых, стало очень сложно выстраивать нужную последовательность, когда какая-нибудь команда создавала другую команду. И в борьбе с этой путаницей родилась идея вложенных списков. Были еще идеи, но они исчезли за ненадобностью.
Конвейер представляла собой уже не две функции, а пару десятков и все они хранились в руте. Сплошная путаница. И тут произошла революция, которую никто не ждал. Во Flash пришло ООП. Причем эта революция произошла не по инициативе Макромедии, она произошла по инициативе сообщества Flash-разработчиков.
Итак, стало очевидным, что десятки функций в руте – это плохо, а правильно для каждого сервиса и механизма создавать классы, которые хранят функции, которые созданы для работы этих механизмов. И они уже являются методами класса. Причем есть методы, которые используются только самим классом, обеспечивают его работу, то есть являются методами реализации, а есть методы для общения с внешним миром, то есть являются интерфейсными методами.
Мое знакомство с ООП, достойно отдельной истории, и сейчас разговор не об этом.
Итак, конвейер стал представлять собой отдельный класс, и вся его работа была реализована внутри него самого. Но изменился не только подход к реализации конвейера, изменилась среда «вокруг» конвейера. Изменилась картина мира. Теперь из Flash «исчезли» клипы, функции и переменные. Вокруг были сплошные классы, объекты, их методы и свойства. Команда трансформировалась из простой ссылки на функцию и ее вызов, в более сложное описание: команда стала хранить ссылку на объект и указание на метод, который нужно вызвать. Плюс появилась новая запись команды: объект и название его свойства, и новое значение этого свойства. Это нововведение избавляло от необходимости создавать новый метод только для того чтобы изменить один параметр. Да и работа просто с функциями оказалась очень живучей конструкцией. Но теперь функции помещались в конвейер как анонимные, и их не существовало нигде кроме как в списке конвейера. Итак, происходили и революционные и эволюционные изменения.
Выход шестой версии Flash изменил и внешнюю среду, и внутреннюю реализацию конвейера. Появилась возможность ассоциировать анонимную функцию и объект с помощью нового метода apply . Это дало возможность создавать виртуальные методы, которые работали с объектом только в момент выполнения команды и сразу же уничтожались. И, наконец, реализовалась старая идея о том, что конвейеров может быть не один, конвейеров может быть несколько. И это стало возможным только при новом подходе к обработке событий входа в кадр и работа со временем через setInterval.
Выход седьмой версии Flash, дал нам AS 2, и опять же конвейер был полностью переписан. Сейчас пока рано говорить о кардинальных изменениях (на данных момент Flash-7 два месяца от роду), однако и сейчас конвейер приобрел новые возможности – значительно расширился функционал внутреннего механизма анимации из точки в точку. Во-первых, анимацию можно создавать по дуге, во-вторых, анимации задается длительность и еще другие нововведения. Так же в очередной раз, была переработан механизм работы с событиями входа в кадр – использована библиотека готовых решений поставляемая с Flash.
Какие выводы можно сделать при данном экскурсе в историю создания конвейера? Во-первых, механизм конвейера возник не на пустом месте. Он создавался и развивался вместе с самим Flash. Появлялись новые возможности и подходы, и механизм конвейера развивался и дополнялся новыми возможностями. Причем, если опять обратиться к Java, есть решения, которые достойны к применению и в Java. Здесь я имею в виду идею иерархического списка исполняемых команд. Но все-таки конвейер, если так можно выразится, Flash-специфичный механизм.
Еще один вывод который хотелось бы сделать: даже изучая новые решения которые возникали при выходе новый версий Flash, и изучая накопленный опыт в Java, могу утверждать что пока на данный момент не создано (применительно к Flash) ничего лучше. Либо эти идеи слишком громоздки (например, описанный чуть раньше паттерн «Command»), либо слишком примитивны (например, просто работа через onEnterFrame, или последовательная подписка на окончания работы между компонентами). Практически при любом сравнении никогда не возникало сомнений в том, что конвейер – оптимальный выбор. Однако, часто, сравнивая с другими решениями, приходилось улучшать конвейер, чтобы он соответствовал новым требованиям.
Но, по сути, конвейер все время оставался простым списком действий.
Следующая часть
[1] [2] [3] [4] [5] [6] [7] |