Как было упомянуто выше, существует большое число образцов проектирования, предназначенных для объектно-ориентированной реализации конечных автоматов в частности и сущностей со сложным поведением в целом. В их основе лежат уже известные читателю два способа описания автоматов в программе: статический (при помощи инструкций выбора и ветвления) и динамический (при помощи таблиц).
Появление полиморфизма подтипов позволяет вместо явного создания таблицы переходов и выходов автомата использовать для этих целей таблицу полиморфных вызовов, в том или ином виде встроенную в любой объектноориентированный язык программирования для диспетчеризации вызовов компонентов. С точки зрения разработчика использование полиморфизма для реализации сложного поведения гораздо удобнее, чем создание таблицы вручную. Образцы проектирования сложного поведения отличаются также способом представления элементов автоматной модели: состояний, событий, действий, переходов. С этой точки зрения одним крайним случаем является «обертывание» автоматной функции в класс, знакомое по предыдущей главе. При этом состояния и события описываются целочисленными константами. Это решение обладает высоким быстродействием, но крайне низкой гибкостью. На другом полюсе находится так называемый шаблон FSM Framework , в соответствии с которым для каждого элемента автоматной модели строится отдельный класс. При этом класс состояния содержит таблицу, ключами в которой являются события, а значениями — переходы. Класс перехода содержит ссылку на целевое состояние и действие. Этот шаблон обеспечивает наибольшую гибкость, однако построенная таким способом программа громоздка и не очень эффективна по времени и памяти. Между двумя полюсами лежат различные комбинации описания элементов автоматной модели с помощью целочисленных констант, подпрограмм и классов. Изучить все многообразие образцов проектирования можно, обратившись к работам В этой книге рассмотрим только два шаблона реализации сложного поведения, которые кажутся авторам наиболее универсальными и согласованными с моделью автоматизированного объекта управления. В контексте этой модели довольно естественно отождествлять события с компонентами автоматизированного класса.
Таким образом, вопрос о представлении событий не возникает. Кроме того, как показывает опыт, для большинства задач не требуется обеспечить возможность динамической конфигурации автомата. Поэтому отдается предпочтение статическому описанию функций переходов и действий. Методы реализации будем рассматривать на примере автоматизированного класса Alarm_Clock, описывающего часы с будильником, причем в целях повышения модульности выделим объект управления в отдельный класс. Наш первый пример показывает, как можно превратить реализацию сущности со сложным поведением из процедурной в объектно-ориентированную, сделав минимум изменений. Часы с будильником теперь представляют собой отдельный класс, компоненты которого соответствуют событиям. Их реализация выполнена уже знакомым читателю способом: с помощью инструкции выбора. В листинге 3.1 приведена реализация объекта управления, а в листинге 3.2 — реализация самого автоматизированного класса на языке C++. Текст вспомогательных классов Time и Bell не относится напрямую к вопросам сложного поведения и в листингах не приводится. Наш второй пример использует в качестве шаблона реализации образец проектирования State. В этом образце вариация поведения в зависимости от состояния реализуется за счет полиморфизма подтипов. Кроме класса, описывающего собственно сущность со сложным поведением, здесь создается абстрактный класс «состояние», компоненты которого также соответствуют событиям. Для каждого конкретного управляющего состояния требуется создать класс-потомок абстрактного состояния и в его компонентах определить поведение сущности, специфичное для данного состояния. Помимо выполнения действий компоненты классов-состояний обновляют значение атрибута next_state (следующее состояние) — так реализована функция переходов. Схема образца проектирования State в форме диаграммы классов приведена на рис. 3.15.В листинге 3.3 содержится реализация автоматизированного класса, описывающего часы с будильником, и классов состояний. Реализация объекта управления остается прежней и поэтому не приведена. В заключение раздела приведем два примера реализации с помощью образца проектирования State классических задач логического управления, которые были рассмотрены в разделе 1.4.2, в рамках парадигмы объектно-риентированного программирования с явным выделением состояний. Эти примеры не имеют значительной ценности с практической точки зрения (напомним, что объектно-ориентированный подход наиболее эффективен для реализации событийных систем), однако они способствуют более глубокому пониманию связи между истоками автоматного программирования и его положением сегодня.
Итак, вновь рассмотрим последовательный двоичный одноразрядный сумматор. Граф переходов автомата Мили, реализующего сумматор, приведен на рис. 1.18.В соответствии с объектно-ориентированным подходом выделим в отдельный класс Position объект управления сумматора. Объектом управления в этой задаче можно считать совокупность текущих двоичных разрядов первого и второго слагаемого, а также результата. В этой системе изначально нет событий, а управляющий автомат является активным. В связи с необходимостью перехода к пассивной автоматной модели искусственно введем единственное событие next_position, которое будет сигнализировать о том, что очередные разряды первого и второго слагаемых готовы и можно вычислять очередной разряд суммы. Диаграмма классов реализации сумматора с помощью образца проектирования State приведена на рис. 3.17, а программный текст — в листинге 3.4 Перейдем к рассмотрению счетного триггера, для которого в разделе 1.4.2 был построен автомат Мура (см. рис. 1.16). В этой системе не будем выделять объект управления в отдельный класс. Вместо этого введем два класса, представляющих соответственно кнопку и лампу. Кроме того, по аналогии с предыдущим примером введем единственное событие tick, которое можно отождествить с приходом сигнала от тактового генератора. Поскольку образец проектирования State предназначен для реализации автоматов Мили, необходимо модифицировать автомат Мура счетного триггера, переместив действия из каждого состояния на все переходы, которые ведут в это состояние. Диаграмма классов реализации счетного триггера на основе образца проектирования State приведена на рис. 3.18, а программный текст — в листинге 3.5.