Применение конечных автоматов при программировании мобильных устройств



Материал из книги Салмре  И. Программирование мобильных устройств на платформе .Net Compact Framework. М.: Вильямс. 2006. Кооперайт на английском языке — Pearson Education, Inc., 2005.

Аналогичные идеи в части применения автоматов в программировании развиваются мною с 1991 года. (http://is.ifmo.ru/works/switch_prr/) — А. Ш. В книге (возможно, только в переводе), почему-то, отсутствует список литературы.

Эта книга становится своего рода библией в быстро развивающемся мире мобильных вычислений. Ее автор работал в компании Microsoft в Редмонде на протяжении 12 лет. Сейчас он работает в Европейском Инновационном Центре компании Microsoft в городе Аахене, Германия, где занимается исследованиями в области разработки программных моделей для современных мобильных устройств.

В этой книге глава 5 (из 17) называется «Наш друг конечный автомат». Учитывая, что автор не является специалистом по конечным автоматам, наличие указанной главы в начале книги свидетельствует о значительной роли автоматов в проектировании мобильных устройств.

Введение

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

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

В эпоху пакетной обработки безраздельно царствовали алгоритмы. Этот период можно назвать эрой «блок-схем».

При построении серверных приложений, отвечающих на запросы, большую роль играет «отсутствие состояния» — нет нужды сохранять состояния между двумя последовательными запросами.

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

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

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

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

Ниже излагается, каким образом применение автоматов может способствовать написанию эффективного и надежного года.

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

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

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

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

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

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

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

Для определения текущего варианта изменения состояния удобно использовать блок операторов switch/case. Каждый оператор case соответствует одному из вариантов изменения состояния и должен содержать вызов функции, выполняющей всю необходимую для этого работу.

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

1. Явно и неявно определенные конечные автоматы

Для написания кода существуют два подхода. (В моих работах высказывается аналогичное мнение (http://is.ifmo.ru/works/mirk/) — А.Ш.)

Подход 1. Зависящее от специфики конкретной ситуации, децентрализованное, неявное управление состояниями (неудачный подход).

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

Подход 2. Плановое, централизованное, явное управление состояниями (удачный подход).

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

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

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

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

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

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

2. Сколько конечных автоматов должно быть в приложении?

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

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

И хотя перечисленные автоматы можно было бы объединить в один «главный автомат», такой автомат представлял бы просто формальное объединение всех возможных случаев изменения состояния приложения и не дал бы ничего нового, поскольку состояния, которыми управляют отдельные конечные автоматы, слабо или вообще не коррелируют друг с другом.

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

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

3. Области применения конечных автоматов при программировании мобильных устройств

3.1. Конечные автоматы и пользовательские интерфейсы

Пользовательские интерфейсы — одна из наиболее очевидных областей, где конечные автоматы могут находить широкое применение. Поведение элементов управления, совместно использующих одну и ту же форму, часто является коррелированным. Некоторые элементы управления отображаются и скрываются в одно и то же время, некоторые элементы перемещаются при отображении других элементов и т.д. (Посмотрите, как предлагается создавать скины для видеоплейера (http://is.ifmo.ru/projects/crystal/) — А.Ш.)

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

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

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

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

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

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

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

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

3.2. Конечные автоматы и управление памятью

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

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

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

3.3. Конечные автоматы и фоновые задачи

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

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

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

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

3.4. Конечные автоматы и игры

Конечные автоматы весьма удобно применять в играх (посмотрите игру «Космонавт» (http://is.ifmo.ru/projects/cosmo/) — А.Ш.), в которых персонажи, перемещающиеся в области игры, могут действовать в различных режимах (например, персонаж бежит вперед или назад, взбирается по лестнице, падает и т.д.).

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

Выводы

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

Старайтесь избегать неявного управления состояниями приложения и не жалейте времени на разработку явно сформированной модели состояний.

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

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