BOLD — инструмент реализации MDA в Delphi

Часть 5. Объектное пространство

Константин Грибачев

Функции Object Space

Основные классы

Описатели (Handles)

Функции Object Space

Понятие объектного пространства (Object Space) является важнейшим элементом в архитектуре Borland MDA. Любой разработчик, использующий эту технологию, рано или поздно неизбежно сталкивается с необходимостью применения на практике многих возможностей, предоставляемых архитектурой MDA-приложений, однако без понимания основ работы с объектным пространством многие из таких возможностей остаются недоступными. В архитектуре MDA-приложений объектное пространство занимает центральный (средний) уровень (рис. 1), называемый бизнес-уровнем или уровнем управления.

Перечислим основные функции бизнес-уровня в Borland MDA:

• хранение информации о модели; обеспечение целостной логической структуры приложения, обработка внутренних событий системы;

• взаимодействие с уровнем представления, обработка внешних событий от пользователя;

• взаимодействие с уровнем данных, обработка внешних событий от СУБД.

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

Для обеспечения таких функций в Borland MDA включены специальные классы-носители информации модели, а также классы, реализующие управление элементами объектного пространства и осуществляющие обработку событий, происходящих в системе. Для иллюстрации решаемых на этом уровне системных задач рассмотрим вопрос контроля и управления жизненным циклом объектов во время работы приложения. При запуске MDA-приложения часть объектов загружается с уровня данных в объектное пространство (необходимость загрузки того или иного объекта может задавать либо разработчик на этапе создания приложения, либо пользователь во время его выполнения — эти вопросы будут освещены в последующих частях цикла). При этом часть объектов остается не загруженной в память, однако приложение считает, что они тем не менее существуют, поскольку не были удалены во время прошлых сеансов работы. Таким образом, формируется совокупность объектов в памяти и объектов в БД, которая образует так называемое концептуальное объектное пространство. Если при этом происходит удаление объекта, он все равно остается в памяти и в БД, пока не будет вызван метод UpdateDatabase. До момента вызова этого метода удаленный объект помечается системой как удаленный и не будет существовать в концептуальном объектном пространстве, став недоступным для использования. Для управления такими ситуациями Borland MDA имеет специальные средства, так называемые машины состояния (State Machines). Подробнее о них мы поговорим в дальнейшем — при описании взаимодействия бизнес-уровня и уровня данных.

В начало В начало

Основные классы

Структура иерархии классов, обеспечивающая функционирование объектного пространства, довольно сложна, объемна и включает сотни классов. Мы рассмотрим только несколько ее наиболее важных фрагментов. Все классы условно можно разбить на две основные категории — классы внутренние и классы внешние.

У внутренних классов предком является класс TObject, а у внешних — класс TComponent.

Фрагмент иерархии внутренних классов представлен на рис. 2.

Кратко опишем некоторые из них.

TBoldMemoryManagedObject — класс-родоначальник этой иерархии классов. Его появление обусловлено тем, что Borland MDA имеет собственный менеджер памяти.

Класс TBoldFlaggedObject предназначен для внутреннего использования и содержит набор битовых значений-флагов, а также набор значений перечисляемого типа, упакованных в целочисленный формат. Флаги в основном используются для сохранения информации о различных состояниях элементов объектного пространства.

Класс TBoldSubscribableObject предоставляет очень важный для Borland MDA механизм подписки на события. Практически любые компоненты Borland MDA, в том числе визуальные, способны выступать в качестве источников определенных событий, на которые можно «подписаться», а по их наступлении автоматически исполнить заранее заданные реакции — обработчики событий. Механизм подписки эффективно используется самой средой Borland MDA, в частности при работе с вычисляемыми (derived) атрибутами и ассоциациями. Этот механизм также доступен и разработчику, предоставляя уникальные возможности по управлению работой приложения в событийно-организованной программной системе.

Класс TBoldElement является важнейшим в представленной иерархии и служит центральным звеном в описании объектного пространства, являясь суперклассом для всех его элементов. Это универсальный класс, способный представлять либо значение, либо собственно объект, либо метаинформацию (информацию о типе). Класс TBoldElement имеет три потомка для представления указанных трех видов представляемых элементов — TBoldValueSetValue (представление значений), TboldDomainElement (представление объектов) и TBoldMetaObject (представление информации о типах).

Класс TBoldSystem представляет объектное пространство в целом, то есть обладает информацией обо всех экземплярах элементов модели приложения.

Класс TBoldObject представляет объект объектного пространства Borland MDA. При генерации кода все пользовательские классы являются потомками TBoldObject. Все объекты в объектном пространстве также являются потомками этого класса либо дочерними по отношению к его потомкам.

Все члены, входящие в состав любого объекта Borland MDA (например, атрибуты), принадлежат к классу TBoldMember. Однако этот класс служит и для других целей, а также имеет самостоятельное значение, порождая, например, такой класс, как TBoldList, который, в свою очередь, является родителем важного класса TBoldObjectList. Как следует из названия, этот класс служит для представления списка объектов.

Чтобы вышеописанная иерархия внутренних классов Borland MDA не выглядела абстрактной схемой, оторванной от реальности, полезно остановиться на классе TBoldObjectList поподробнее и показать на его примере, как на практике реализовать программный доступ к элементам объектного пространства из приложения во время его выполнения. Каждый экземпляр класса TBoldObjectList представляет собой список объектов (экземпляров) определенного класса модели приложения. Здесь можно провести некоторую грубую аналогию с базой данных: список объектов класса TBoldObjectList, состоящий из объектов (на самом деле из указателей на объекты), можно рассматривать как таблицу БД, состоящую из записей. При этом каждый объект в списке имеет набор атрибутов, а каждая запись в таблице БД имеет набор полей.

Вспомним теперь наш пример с приложением — библиотечным каталогом из 4-й части данного цикла — и поставим следующую задачу: программно, в процессе работы приложения, получить фамилию первого автора в каталоге. Напомним, что в нашей модели присутствует класс с названием (Expression Name) «Avtor». Для доступа к списку всех объектов класса «Avtor» используем следующее выражение:

BoldSystemHandle1.System.ClassByExpressionName[‘Avtor’];

Здесь BoldSystemHandle1 — компонент Borland MDA, отвечающий за управление объектным пространством, System — свойство этого компонента типа TBoldSystem (см. выше и рис. 1), ClassByExpressionName — метод класса TBoldSystem, возвращающий выражение типа TBoldObjectList (в данном случае — список всех объектов-экземпляров класса «Avtor» модели).

Класс TBoldObjectList имеет свойство BoldObjects[i] — типа TBoldObject, указывает на объект в списке с порядковым номером i (при этом первый объект в списке имеет номер 0). В свою очередь, класс TBoldObject обладает свойством BoldMemberByExpressionName[‘имя_члена’], позволяющим получить доступ к заданному атрибуту (члену класса). Теперь мы можем сформировать полное выражение-оператор для решения нашей задачи:

BoldSystemHandle1.System.ClassByExpressionName[‘avtor’].BoldObjects[0].BoldMemberByExpressionName[‘fio’].AsString;

Читатель может самостоятельно проверить работоспособность данного выражения на практике, поместив приведенный текст оператора в обработчик какого-нибудь события и присвоив его значение тексту какой-нибудь метки (Label), а также поэкспериментировать с ним для чтения либо для изменения других атрибутов или объектов.

Таким образом, рассмотренный пример дает возможность сделать следующие важные выводы:

• каждому классу модели соответствует свой объект-экземпляр класса TBoldObjectList объектного пространства;

• используя свойства класса TBoldObjectList, можно программным образом читать, а также изменять значения атрибутов объектов, являющихся экземплярами классов модели.

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

Возвращаясь к иерархии внутренних классов (см. рис. 2), стоит обратить внимание на класс TBoldAttribute, являющийся родительским классом (суперклассом) для нескольких классов, представляющих атрибуты различных типов — строковых (TBAString), даты (TBADate), битовых массивов BLOB (TBABlob) и т.д. Таким образом, мы видим, что Borland MDA имеет свои типы данных — аналоги стандартных Delphi-типов. И это не случайно, поскольку Borland MDA представляет собой своего рода систему в системе, то есть самостоятельную программную систему, функционирующую в рамках определенной операционной системы (в настоящее время Windows) и обладающую собственным менеджером памяти, собственным развитым механизмом генерации и обработки событий, а также другими подобными свойствами.

При желании разработчик может создать и зарегистрировать новый MDA-тип данных, воспользовавшись мастером создания атрибутов (в меню Delphi выбрать Bold > Attribute Wizard…).

Теперь кратко рассмотрим иерархию внешних классов (рис. 3).

Все внешние классы имеют в качестве родительского класс TComponent. Это, естественно, означает, что большинство таких классов мы увидим на палитре компонентов среды разработки Delphi. Если внутренние классы можно рассматривать как своего рода «скелет», поддерживающий сложный «организм» объектного пространства, то внешние классы скорее являются своеобразными «адаптерами-переходниками» к уровню представления и уровню данных, а также основными инструментами разработчика Borland MDA-приложений. Уровни представления и данных мы рассмотрим в последующих частях статьи более детально. Сейчас лишь обратим внимание на тот факт, что большинство внешних классов содержит термин «Handle», значение которого в контексте нашего рассмотрения целесообразно перевести как «описатель».

В начало В начало

Описатели (Handles)

При разработке MDA-приложений в среде Delphi описатели занимают, пожалуй, центральное место. Какие бы функции разработчик ни реализовывал — будь то вывод информации из объектов в табличном виде, сортировка или фильтрация данных, специальные запросы к СУБД и т.д. — все они реализуются посредством описателей. Разрабатывая в предыдущих частях простое MDA-приложение, мы тоже использовали описатели списков TboldListHandle.

Описатели Borland MDA подразделяются на два основных типа: корневые (root) и производные (rooted). На рис. 3 основные корневые описатели выделены сиреневым цветом, а производные — зеленым.

Корневые описатели являются, как легко догадаться по их названию, первичными источниками информации об объектном пространстве, в то время как любой производный описатель обязательно обладает свойством RootHandle (корневой описатель). В качестве значения этого свойства не обязательно должен выступать корневой описатель, то есть производные описатели могут объединяться в цепочки; при этом указанное свойство «RootHandle» последующего члена цепочки указывает на предыдущий член. Однако у первого члена такой цепочки это свойство должно указывать на корневой описатель.

Демонстрация и практические примеры работы с описателями требуют знания основ языка OCL (Object Constraint Language — язык объектных ограничений), который является одним из самых мощных средств Borland MDA. Основам использования OCL для работы с объектным пространством будет посвящена следующая часть этой статьи.

КомпьютерПресс 9'2003