Турнирная таблица
Описание задачи "Турнирная таблица"
Создаваемая с помощью платформы lsFusion информационная система должна содержать функциональность для ведения турнирной таблицы хоккейного турнира.
Под турниром понимается подмножество игр между командами (участниками каждой из игр являются 2 команды), по результатам которых командам начисляются очки.
Результатом каждой игры может быть победа одной из команд в основное время (команде-победителю начисляется 3 очка), победа в овертайме (к оманде-победителю начисляется 2 очка, а проигравшей команде 1 очко) и победа в серии буллитов (команде-победителю начисляется 2 очка, а проигравшей команде 1 очко).
Рейтинговое место команды в турнирной таблице определяется количеством набранных очков, а в случае их равенства дополнительными параметрами: количеством побед в основное время, количеством побед в овертайме, количеством побед в серии буллитов, разницей заброшенных и пропущенных шайб, количеством заброшенных шайб. Дополнительные параметры для определения итогового места последовательно применяются в заданном порядке до достижения ситуации, при которой результаты команд ранжируются однозначно.
Задание предметной логики
Объявление модуля
Объявляем модуль, в рамках которого будет реализован требуемый функционал. Присваиваем модулю произвольное наименование (например, `HockeyStats1).
MODULE HockeyStats;
Зададим использование в модуле HockeyStats
функциональности из других модулей. В частности, нам понадобится системный модуль System
, в котором объявляются некоторые используемые в примере элементы системы.
REQUIRE System;
Определение команды
Вводим понятие команды, для чего создаем отдельный класс с помощью соответствующей инструкции CLASS
.
CLASS Team 'Команда';
Создаваемому классу присваиваем название (например, Team
), которое в дальнейшем будет использоваться при построении выражений, а также подпись для отображения на пользовательских формах (например, 'Команда'
).
Чтобы при работе с созданными позднее формами все команды можно было легко идентифицировать, создаем для команды название. Иными словами, создаем свойство "Название", которое может быть определено для объектов класса Team
.
name 'Название команды' = DATA STRING[30] (Team) IN base;
Таким образом, название команды является первичным (вводимым пользователем) свойством строчного типа. Опцией IN
созданное свойство добавляется в предопределенную группу свойств base
. Свойства объекта, относящиеся к группе base
, будут автоматически отображаться на диалоговой форме для выбора объекта класса Team
.
Определение игры
Вводим понятие игры и ее атрибуты: дата, участники игры (команда хозяев и команда гостей) и их названия.
CLASS Game 'Игра';
date 'Дата' = DATA DATE (Game);
hostTeam = DATA Team (Game);
guestTeam = DATA Team (Game);
hostTeamName 'Хозяева' (Game game) = name(hostTeam(game));
guestTeamName 'Гости' (Game game) = name(guestTeam(game));
Свойства hostTeam
и guestTeam
являются первичными объектными свойствами игры, дающими в качестве результата ссылку на команду хозяев и команду гостей соответственно (т.е. на конкретные объекты класса Team
). Свойства названий команды хозяев и гостей игры (hostTeamName
и guestTeamName
) создаются для использования в дальнейшем на формах. Если на форму выносить сами свойства hostTeam
и guestTeam
, то пользователю будут отображаться внутренние идентификаторы объектов из базы данных.
Введем ограничение, что участниками игры должны быть две разные команды.
CONSTRAINT hostTeam(Game team) = guestTeam(team) CHECKED BY hostTeam, guestTeam MESSAGE 'Хозяйская и гостевая команды должны быть разными';
Механизм работы данного выражения следующий: при изменении у игры команды хозяев или команды гостей система проверяет условие равенства этих команд hostTeam(team) == guestTeam(team)
и в случае его выполнения блокирует применение изменений в базу данных, а также выдает пользователю заданное сообщение 'Хозяйская и гостевая команды должны быть разными'
. Иными словами, результатом выражения, заданном после оператора CONSTRAINT
, должен быть NULL
. В иных случаях ограничение будет считаться нарушенным. Кроме того, благодаря конструкции CHECKED BY
, добавленное ограничение будет фильтровать исходя из заданного правила команды при выборе для игры команды хозяев или команды гостей (т.е. в появляющемся диалоговом окне исключать из перечня команд уже заданную в качестве соперника команду).
Задаем количество голов, забитых каждой из команд за время игры.
hostGoals 'Х голы' = DATA INTEGER (Game);
guestGoals 'Г голы' = DATA INTEGER (Game);
В заданных свойствах используется тип INTEGER
, поскольку количество забитых голов каждой из команд является целым числом.
Вводим ограничение, что игра не может закончиться вничью. Система должна запретить пользователю задавать одинаковое количество голов для обоих команд игры, выдавая при этом сообщение с заданным текстом.
CONSTRAINT hostGoals(Game game) = guestGoals(game) MESSAGE 'Игра не может закончиться вничью';
Определение победителя игры
Определяем победителя игры - команду, которая забила голов больше, чем соперник.
winner(Game game) = IF hostGoals(game) > guestGoals(game)
THEN hostTeam(game)
ELSE guestTeam(game);
В данном случае используется оператор IF... THEN... ELSE
, который проверяет верность условия, что команда хозяев в данной игре забила больше голов, чем команда гостей, и в случае его выполнения победителем игры является команда хозяев, в противном случае - команда гостей.
По аналогичному принципу команда, участвовавшая в игре и забившая голов меньше соперника, будет считаться проигравшей.
looser(Game game) = IF hostGoals(game) > guestGoals(game)
THEN guestTeam(game)
ELSE hostTeam(game);
Определение результата игры
Вводим понятие возможного результата игры с предопределенным набором значений: победа в основное время, победа в овертайме, победа в серии буллитов.
CLASS GameResult 'Р/И' {
win 'П',
winOT 'ПО',
winSO 'ПБ'
}