На прошлой неделе мы завершили разработку прототипа нашей игры. Большинство авторов статей по программированию, обычно, этими и ограничиваются, полагая, что все самое интересное рассказано. На мой взгляд, интересное только начинается...
6. Проектируем БД
Давайте попробуем привести наш набросок игры к более менее товарному виду (продать его разумеется не удастся, ввиду наличия на Android Market несметного количества совершенно аналогичных бесплатных приложений, но мы ведь говорим о разработке?).
Вновь сталкиваясь с "ужасом чистого листа", вспоминаем, что разработку любого мало мальски сложного приложения, нужно начинать с проектирования его данных. Нужна ли нашему приложению БД? На мой взгляд, несомненно (нужно же нам где-то сохранять текущее состояние при выгрузке приложения, да и сами головоломки необходимо где-то хранить).
Платформа Android заботливо решает за нас непростой вопрос выбора СУБД для хранения данных, предоставляя в наше пользование SQLite. Хотя эту СУБД вряд-ли стоит использовать для организации к БД многопользовательского доступа, для разработки встраиваемых БД она подходит идеально (за исключением легких неприятностей с сортировкой кирилицы, единственным известным мне недостатком SQLite явлется несколько необычное понимание того, чем являются транзакции, что делает практически невозможным изменение данных в БД SQLite несколькими пользователями одновременно).
Разработку БД я рекомендую начинать с ER-диаграммы. Ее значение настолько трудно переоценить, что даже если бы ее пришлось рисовать в Paint-е, это стоило бы сделать, чтобы иметь перед глазами наглядую схему того как организованы данные, когда придется составлять SQL-запросы. Разумеется, гораздо лучше использовать специализированное CASE-средство.
Эта диаграмма обескураживает o O
И вызывает законные вопросы:
- Кто все эти таблицы?
- И неужели все это действительно нужно???
Допуская вероятность, что я действительно мог в этом отношении несколько переусердствовать, в оставшейся части статьи я постараюсь описать все эти таблицы.
Locale
Хотя Anroid предоставляет великолепные средства для локализации строковых ресурсов, в нашем проекте мы этими средствами воспользоваться не сможем, поскольку часть строковых ресурсов будет связана с головоломками сохраненными в БД и возможно создаваемыми самим пользователем (то есть не будет доступна на момент сборки приложения). Описания доступных локалей мы будем хранить в таблице Locale. В поле name будем хранить наименование локали в Java (для возможности автоматического определения текущей локали), поле is_default будет задавать локаль по умолчанию, а string_id будет ссылаться на наименование локали, используемое в нашем приложении.
String
Таблица, содержащая идентификаторы всех доступных в приложении строковых ресурсов. Скорее всего, эта таблица никогда не будет использоваться в наших SQL-запросах, и можно было бы ее не создавать, но без нее ER-диаграмма станет более чем непонятной. Ну и кроме того, вдруг мы решим использовать внешние ключи???
String_Value
Конкретные значения строковых ресурсов с учетом их локали. В locale_id хранится идентификатор локали в string_id идентификатор строкового ресурса, а в value, собственно строковое значение.
Puzzle
В этой таблице будут храниться наши головоломки (по одной строке на каждую). Наименование головоломки будет храниться в string_id в виде ссылки на строковый ресурс, а начальна расстановка (что бы мы сейчас под ней не понимали) будет определяться значением start_position_id.
End_Position
Мы вполне могли бы хранить финальную позицию (ту при достижении которой головоломка считается решенной) в puzzle.end_position_id, если бы такая позиция была одна :) Вообще говоря может быть несколько равноценных позиций, при достижении любой из которых, головоломка считается решенной.
Поскольку мы имеем дело с соотношением 1:N, создаем таблицу в которой будем хранить идентификатор головоломки puzzle_id и идентификаторы ее конечных позиций position_id.
Param
Параметры головоломки (их как минимум 2: размер игрового поля по вертикали и горизонтали). Тип параметра будет определяться значением param_type_id, а значение будет храниться в value. В puzzle_id, разумеется, будет сохраняться идентификатор головоломки, к которой относятся эти параметры.
Param_Type
Снова таблица, которая вряд-ли будет участвовать в наших SQL-запросах. В ней будут храниться типы возможных параметров (все два) и без нее param.param_type_id будет смотреть в пустоту :(
Tag
Описания наших плашек будут храниться в таблице tag. Если подумать, у каждой плашки не так много собственных атрибутов. Помимо принадлежности к конкретной головоломке puzzle_id это уникальный (в пределах головломки) индекс, идентифицирующий плашку (поскольку глобально уникальым первичным ключем id пользоваться может быть несколько неудобно).
Item
Таблица item хранит составляющие наших плашек (элементарные квадратики). В tag_id хранится идентификатор плашки, в x и y относительные координаты item-а по горизонтали и вертикале. По крайней мере один item в каждом tag должен будет иметь координаты <0, 0>.
Tag_Position
Третья наша главная таблица, в которой будут сохраняться координаты tag-ов (если быть точнее, их item-ов с координатами <0, 0>) в некоторой позиции. Таблица содержит идентификатры tag-а и позиции (tag_id и position_id), а также координаты x и y (начиная с 1). Таким образом, координаты item-а будут вычисляться как <item.x + tag_position.x, item.y + tag_position.y>.
Следует заметить, что позиция совершенно не обязана описывать координаты всех tag-ов головоломки, что будет нами использоваться при описании финальных позиций.
Position
Еще одна неиспользуемая таблица, чтобы position_id было на что ссылаться.
Profile
Таблица, которая, скорее всего, всегда будет содержать одну и только одну запись (в которой is_default будет равно 1). Но должны же мы где-то сохранять текущие настройки, на время выгрузки приложения?
В puzzle_id мы будем хранить идентификатор текущей головоломки, а в locale_id - текущей локали. Ну а если нам когда нибудь придет в голову добавить поддержку пользовательских профилей, мы будем знать, куда добавить записи (тут-то и login пригодится).
Session
Таблица session также будет хранить текущие настройки. Помимо идентификатора профиля profile_id, в ней будет храниться идентификатор головоломки puzzle_id и идентификатор текущей позиции position_id. Эта таблица будет хранить несколько (возможно 0) записей. Текущая сессия будет помечена флагом is_current, а закрытые сессии (для таблицы рекордов) будут помечены is_closed. Строго говоря, хватило бы и одного из этих атрибутов, но мне лень перерисовывать диаграмму :)
Stat
Эта таблица будет содержать значения параметров связанных с сессией. Пока я могу придумать только один такой параметр - количество выполненных ходов (напомню, что по времени мы нашего пользователя решили никак не ограничивать, и это серьезное маркетинговое решение). Помимо идентификатора сессии session_id, в таблице определено поле stat_type_id (вдруг еще какие-то типы придумаем) и value, в котором будем хранить значение параметра. К слову сказать, тип поля value мы выбрали неправильно, но об этом мы узнаем только когда приступим к реализации таблицы рекордов. Пока-же мы пребываем в блаженном неведении (я ведь уже говорил, что мне лень все это перерисовывать???).
Stat_Type
Если есть типы, их надо где-то хранить. Я настаиваю на этом :)
Solution_Step
Ну и наконец последня наша таблица, в которой мы будем сохранять выполненные в рамках сессии ходы. Она сильно нам поможет, если мы захотим выполнять Undo/Redo после перезагрузки приложения. Помимо session_id, она будет содержать tag_id, приращения его координат в рамках хода dx и dy (возможно отрицательные, но одно из них должно будет равняться 0) и порядковый номер хода ord_num, уникальный в пределах сессии (наименование возможно не сильно удачно, но фантазия в этом месте кончилась).
Уфффф... Теперь, когда мы представляем себе, что собираемся хранить, мы можем закодировать все это в Java, чем и займемся в следующей статье.
Комментариев нет:
Отправить комментарий