Трансформирующие системы осуществляют некоторое преобразование входных данных и после этого завершают свою работу. В таких системах, как правило, входные данные полностью известны и доступны на момент запуска системы, а выходные — только после завершения ее работы. К трансформирующим системам относятся, например, архиваторы и компиляторы.
Интерактивные системы взаимодействуют с окружающей средой в режиме диалога (например, текстовый редактор). Характерной особенностью таких систем является то, что они могут контролировать скорость взаимодействия с окружающей средой — заставлять среду «ждать». Реактивные системы взаимодействуют с окружающей средой путем обмена сообщениями в темпе, задаваемом средой. К этому классу можно отнести большинство телекоммуникационных систем, а также системы контроля и управления физическими устройствами. Читателю наверняка известно, что конечные автоматы в программировании традиционно применяются при создании компиляторов, которые относятся к классу трансформирующих систем. Автомат здесь понимается как некое вычислительное устройство, имеющее входную и выходную ленты. Перед началом работы на входной ленте записана строка, которую автомат далее посимвольно считывает и обрабатывает. В результате обработки автомат последовательно записывает некоторые символы на выходную ленту. Другая традиционная область использования автоматов — задачи логического управления — является подклассом реактивных систем. Здесь автомат — это, на первый взгляд, совсем другое устройство. У него несколько параллельных входов (чаще всего двоичных), на которые в режиме реального времени поступают сигналы от окружающей среды. Обрабатывая эти сигналы, автомат формирует значения нескольких параллельных выходов.
Таким образом, даже традиционные области применения конечных автоматов охватывают принципиально различные классы программных систем. Авторы книги убеждены, что в действительности круг задач, при решении которых целесообразно использовать автоматный подход, значительно шире и включает создание программных систем, принадлежащих всем трем перечисленным классам. Однако автоматные модели, используемые при создании различных видов программных систем, могут отличаться друг от друга.
По мнению авторов, критерий применимости автоматного подхода лучше всего выражается через понятие «сложное поведение». Неформально можно сказать, что сущность (объект, подсистема) обладает сложным поведением, если в качестве реакции на некоторое входное воздействие она может осуществить одно из нескольких выходных воздействий. При этом существенно, что выбор конкретного выходного воздействия может зависеть не только от входного воздействия, но и от предыстории. Для сущностей с простым поведением реакция на любое входное воздействие зависит только от этого воздействия1 (рис. 1.1).Рассмотрим в качестве примера электронные часы (рис. 1.2). Пусть у них имеется только две кнопки, которые предназначены для установки текущего времени: кнопка «H» (Hours) увеличивает на единицу число часов, а кнопка «M» (Minutes) — число минут. Увеличение происходит по модулю 24 и 60 соответственно.
*1 Сложное поведение также называют поведением, зависящим от состояния (в англоязычной литературе используется термин state-dependent behavior). Соответственно, простое поведение можно назвать поведением, не зависящим от состояния. когда будет рассмотрено понятие управляющих состояний. Такие часы обладают простым поведением, поскольку каждое из двух входных воздействий (нажатие первой или второй кнопки) приводит к единственной, заранее определенной реакции часов. Рассмотрим теперь электронные часы с будильником (рис. 1.3). Дополнительная кнопка «A» (Alarm) предназначена в них для включения и выключения будильника. Если будильник выключен, то кнопка «A» включает его и переводит часы в режим, в котором кнопки «H» и «M» устанавливают не текущее время, а время срабатывания будильника. Повторное нажатие кнопки «A» возвращает часы в обычный режим. После этого, если текущее время совпадает со временем будильника, включается звонок, который отключается либо нажатием кнопки «A», либо самопроизвольно через минуту. Наконец, нажатие кнопки «A» в обычном режиме при включенном будильнике приводит к выключению будильника. Поведение часов с будильником уже является сложным, поскольку одни и те же входные воздействия (нажатие одних и тех же кнопок) в зависимости от режима инициируют различные действия. В программных и программно-аппаратных вычислительных системах сущности со сложным поведением встречаются очень часто. Таким свойством обладают устройства управления, сетевые протоколы, диалоговые окна, персонажи компьютерных игр и многие другие объекты и системы. Распознать сущность со сложным поведением в исходном коде программы можно следующим образом: при традиционной реализации таких сущностей используются логические переменные, называемые флагами, и многочисленные запутанные конструкции ветвления, условиями в которых выступают различные комбинации значений флагов. Такой способ описания логики сложного поведения плохо структурирован, труден для понимания и модификации, подвержен ошибкам. Даже для такого элементарного примера, как часы с будильником, реализация при помощи флагов выглядит громоздко и непонятно (листинг 1.1). Одна из центральных идей автоматного программирования состоит в отделении описания логики поведения (при каких условиях необходимо выполнить те или иные действия) от описания его семантики (собственно смысла каждого из действий). Кроме того, описание логики при автоматном подходе жестко структурировано. Эти свойства делают автоматное описание сложного поведения наглядным и ясным. Основная рекомендация по применению автоматного программирования очень проста: используйте автоматный подход при создании любой программной системы, в которой есть сущности со сложным поведением. Опыт показывает, что таким свойством обладает практически любая серьезная система. Однако обычно не все компоненты системы характеризуются сложным поведением. Поэтому данную выше рекомендацию можно дополнить еще одной: используйте автоматный подход при создании только тех компонентов системы, которые являются сущностями со сложным поведением. Это дополнение призывает не перегружать спецификации и код описанием логики там, где в этом нет необходимости. Следование этим простым рекомендациям позволяет создавать корректные и расширяемые программные системы со сложным поведением. Однако сделать это не так просто. Идентификация сущностей со сложным поведением на этапе проектирования системы, а также выделение логики их поведения — сложные творческие задачи. Эти нетривиальные проектные решения принимаются разработчиком для каждой задачи индивидуально.