Вне зависимости от методов разработки любая программа обладает состояниями, которые в каждый момент времени определяются значениями всех ее данных. Как пишет Гради Буч, «внутри большой прикладной программы могут существовать сотни и даже тысячи переменных и несколько потоков управления. Полный набор этих переменных описывает состояние прикладной программы в каждый момент времени» [1].

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

Возможно, что разработчик предусмотрит реакции программы на все комбинации значений управляющих переменных (2n комбинаций в рассматриваемом случае). Однако более вероятно, что сколько-то (вплоть до 2n-n) комбинаций он не учтет, и тогда при неожиданном сочетании входных воздействий программа способна перейти в непредусмотренное, или, по терминологии Фредерика Брукса, «невизуализируемое», состояние: «Сложность служит причиной трудности перечисления, а тем более понимания всех возможных состояний программы, а отсюда возникает ее ненадежность... Сложность структуры является источником невизуализируемых состояний, в которых нарушается система защиты» [2].

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

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

Так, игру Starship Troopers компании BlueTooth можно смело назвать одной из лучших за 2000 г.: у нее прекрасные графика и звук, популярный сюжет, а игровой процесс очень хорош. Однако в редком (и, видимо, именно поэтому ускользнувшем от внимания разработчиков) случае, когда одного из десантников, находящихся под управлением игрока, во время полета с помощью реактивного ранца атакует «птичка», можно наблюдать довольно странную картину боя: в окне состояний бойцов десантник отображается как погибший, но вмиг потеряв весь свой запас брони и жизненных сил, он выглядит и действует ничуть не хуже своих «более живых» товарищей.

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

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

SWITCH-технология: общие сведения

Идея описывать с помощью многозначных переменных состояния как развитие сюжета игры, так и взаимодействие игрока с персонажами, управляемыми компьютером, напрашивается сама собой. В играх компании BioWare (например, Baldur?s Gate 2) эти переменные используются для описания последовательностной логики развития сюжета игры. Предложение применять автоматы для описания поведения в играх можно найти, например, в [3]. Но этот подход, конечно, был известен и ранее [4].

Технология, основанная на многозначных переменных состояния, получившая название «SWITCH-технология», была предложена в 1991 г. А.А. Шалыто для алгоритмизации и программирования задач логического управления [5]. Именно в ней был впервые введен этап кодирования состояний, отсутствующий в традиционных технологиях программирования. Позднее автор развивал SWITCH-технологию применительно к событийным («реактивным») системам [6, 7].

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

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

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

В качестве графической нотации были выбраны графы переходов смешанных автоматов [5], так как компактность и наглядность сочетаются в них с гибкостью, позволяющей без труда вводить дополнительные обозначения и вносить изменения (рис. 1).

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

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

Рис. 3. Панель инструментов

Проиллюстрируем особенности описываемого метода на примере. Создадим двумя способами — традиционным и с применением SWITCH-технологии — программный модуль для управления распространенным элементом интерфейса — панелью инструментов (рис. 3). Он будет реализовывать две функции: перемещение панели при нажатой правой кнопке мыши и вывод меню панели нажатием и отпусканием (щелчком) правой кнопки мыши.

Традиционный подход

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

  • нажатие правой кнопки мыши;
  • отпускание правой кнопки мыши;
  • перемещение мыши с нажатой правой кнопкой;
  • выход курсора мыши за границу панели.

Листинг 2 содержит модуль, состоящий из обработчиков перечисленных событий, для ОС QNX и графической оболочки Photon.

На первый взгляд эти обработчики кажутся независимыми. Так обычно считается и в литературе по программированию. Однако в действительности обработчики связаны друг с другом уже в силу того, что предназначены для управления одним и тем же объектом (панелью). Oни также содержат общую управляющую переменную menu. Как следует из текста программы, логика ее работы распределена по обработчикам событий, а значит, поведение модуля априори непредсказуемо.

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

Аналогичный эффект возникает и тогда, когда самодокументирующимся делают визуальный формализм. Примером могут служить диаграммы состояний (Statecharts), используемые в языке UML [8]: запись сложных логических выражений, а также большого числа выходных воздействий и их особенностей становится практически необозримой. То же происходит при работе с SDL-диаграммами, широко применяемыми при программной реализации протоколов в телефонии [7].

Отметим, кроме того, что при традиционном написании текстов программ реализация функций входных и выходных воздействий обычно выполняется совместно с логикой, затрудняя ее понимание. Иначе говоря, даже если написанная так программа является структурированной (не содержит операторов goto), ее логика «структурированной» не является.

Окончание в следующем номере.

Литература

1. Буч Г. Объектно-ориентированный анализ и проектирование с примерами приложений на С++. М.: Бином, СПб: Невский диалект, 1998. 560 с.

2. Брукс Ф. Мифический человеко-месяц, или Как создаются программные системы. СПб.: Символ, 2000. 304 с.

3. Секреты программирования игр /А. Ла Мот, Д. Ратклифф, М. Семинаторе и др. СПб.: Питер, 1995. 278 с.

4. Дейкстра Э. Взаимодействие последовательных процессов // Языки программирования. М.: Мир, 1972, с.9—86.

5. Шалыто А.А. SWITCH-технология. Алгоритмизация и программирование задач логического управления. СПб.: Наука, 1998. 628 с.

6. Шалыто А.А., Туккель Н.И. SWITCH-технология — автоматный подход к созданию программного обеспечения «реактивных» систем // Промышленные АСУ и контроллеры, 2000, №10, с.44—48.

7. Шалыто А.А. Алгоритмизация и программирование для систем логического управления и «реактивных» систем // Автоматика и телемеханика, 2001, №1, c.3—39.

8. Буч Г., Рамбо Д., Джекобсон А. Язык UML. Руководство пользователя. М.: ДМК, 2000. 432 с.

ОБ АВТОРАХ

Анатолий Абрамович Шалыто — ученый секретарь ФНПЦ ГУП «НПО «Аврора»» (Санкт-Петербург), профессор кафедры «Компьютерные технологии» СПбГИТМО (ТУ). E-mail: aurora@peterlink.ru (для Шалыто).

Никита Иосифович Туккель — инженер-программист ФНПЦ ГУП «НПО «Аврора»» (Санкт-Петербург). E-mail: cynical@mail.ru