Создание MDA-приложений с использованием машин состояний

Для примера возьмем уже созданное нами MDA-приложение в лабораторной работе №1 и дополним его машиной состояний. Расширим нашу предметную область. Добавим новый класс Дисциплина. Теперь у преподавателей появится список преподаваемых ими дисциплин. Введем ограничение на количество рабочих часов в семестр для преподавателя. Задача: запрограммировать процесс распределе­ния нагрузки на преподавателей.

Использование диаграмм состояний UML позволяют визу­ально проектировать последовательности смены состояний объектов программы в зависи­мости от условий.

Модификация модели UML

1) Откроем проект projDeanOffice.

2) Добавим к модельному пространству приложения класс clSubject (Дисциплина). Определим поля класса: поле SubjName (название дисциплины) типа String, поле SubjType (тип дисциплины) типа String, поле SubjAmountHours (количество часов дисциплины) типа Integer.

3) В существующий класс Преподаватель добавим атрибут MaxAmountHours (ограничение на количество рабочих часов) типа Integer.

4) Теперь добавим вычисляемое поле. Вычисляемое поле – это поле, значение которого не хранится в классе, а рассчи­тывается непосредственно в момент обращения к нему. Добавим в класс Преподаватель атрибут LectAmountHours (количество рабочих часов в семестр) типа Integer. Перед именем поля поставим символ /, визуально обозначающий вычисляемое значение. Признаком вычисляемости поля является значение True для свойства Derived (вычисляемое). Значение свойства LectAmountHours будет формироваться выражение OCL. Нужное выражение должно быть значением свойства Derivation OCL (Код OCL для вычис­ления). Это выражение введем позже, когда создадим машину состояний.

5) Сформируем между созданными классами ассоциативное отношение: «один преподаватель – много дисциплин». Дадим сторонам связи названия. В свойство Name для сторон End1 и End2 введем roleLecturer и roleSubjects соответственно (см. Рисунок 9.1).

Рисунок 9.1 – Модификация модели UML

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

Создание машины состояний

В нашем примере дополним класс Дисциплина новыми возможностями. Каждая дисциплина будет иметь определенный статус. Возможно добавление дисциплины преподавателю (состояние Выбран преподаватель). После чего она может быть принята на рассмотрение (состояние Выбрана дисциплина), затем либо назначена преподавателю (состояние Назначена), либо отклонена (состо­яние Отклонена). Состояние Отклонена дисциплина также получит, если сумма часов предложенных преподавателю дисциплин превысит допустимую нагрузку на преподавателя. В случае отклонения дисциплины рассматриваемый экземпляр класса Дисциплина автоматически удаляется. Покажем, как реализовать такой алгоритм исключительно на уровне модели, не прибе­гая к кодированию на языке Delphi.

1) Перейдем в модельное пространство проекта. Выберем элемент clSubject и с помощью контекстого меню дадим команду Add > ECO State Machine. В рамках Проектировщика откроется новое, первоначально пустое окно. В нем будет построена соответству­ющая модель. Назовем модель StatesOfSubject.

2) Поместим на диаграмму начальное состояние Initial.

3) Добавим на диаграмму четыре основных состояния дисциплины с помощью инструмен­та State. Дадим им имена: ChosenLecturer (Выбран преподаватель), ChosenSubject (Выбрана дисциплина), Appointed (Назначена), Rejected (Отклонена).

4) Поместим на диаграмму конечное состояние Final.

5) Согласно проектной логике, свяжем эти состояния отношением-переходом с помо­щью инструмента Transition (см. Рисунок 9.2).

Рисунок 9.2 – Диаграмма машины состояний

6) Вернемся на диаграмму классов ЕСО. Видно, что в классе Дисциплина появилось новое свойство State_1 типа String (назовем его SubjectState), обозначающее нашу машину состояний. Теперь можно ввести выражение OCL для вычисляемого поля Количество рабочих часов (LectAmountHours) класса Преподаватель. В свойстве Derivation OCL этого атрибута создадим с помощью встроенного OCL-редактора выражение:

self.roleSubjects->select(SubjectState='Appointed').SubjAmountHours->sum

Оно означает, что в поле Количество рабочих часов каждого объекта Преподаватель будет сумма часов тех дисциплин, которые ему уже назначены (поле SubjectState имеет значение Appointed).

7) Для каждого перехода надо задать триггер.

В терминологии модели ЕСО триггер— это действие, которое производится при выполнении определенного условия и модифицирует текущее состояние (вызыва­ет переход к другому состоянию).

Добавим в класс Дисциплина три триггера. Каждый триггер добавляют командой контекстного меню Add > Trigger. Дадим этим триггерам соответствующие имена: Choose, Appoint и Reject. Их свойства Alias заполним русскоязычными названиями: Выбрать, Назначить и Отклонить (см. Рисунок 9.3).

Рисунок 9.3 – Список возможных состояний класса Дисциплина

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

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

9) Зададим правило перехода из состояния Выбрана дисциплина в состояние Назначена. Пока что ограничений или дополнительных требований на этот переход не наложено, и пользователь может бесконтрольно менять состояние дисциплины. Правило перехода задается в свойстве Guard (сторожевое условие) в виде условного выражения OCL:

(self.roleLecturer.LectAmountHours+self.SubjAmountHours)<=self.roleLecturer.MaxAmountHours

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

Рисунок 9.4 – Добавляем триггеры и сторожевое условие к диаграмме машин состояний

Обновление базы данных

1) Чтобы сгенерировать исходный код ECO в соответствие с построенной моделью выберем операцию Regenerate ECO source code из консольного меню окна Model View. Также такая операция производится нажатием кнопки над данным окном. Генерация производится автоматически.

2) Так как мы создали новый класс и добавили новые поля в уже существующий, необходимо обновить подключенную базу данных. Для этого перейдем на закладку настройки объектного пространства projDeanOfficeEcoSpace. Нажмем кнопку Generate Schema в нижней части окна. В текущей базе данных автоматически сформируются таблицы, содержащие поля для хранения модели проекта. При этом все имеющиеся данные в базе удаляются. Операция Evolve Schema позволяет сохранить существующие данные и дополнить базу созданными элементами, в соответствие с построенной моделью.