Bold for Delphi. Частина 2, Комерція, Різне, статті





Введення
Ми злітаємо
Збереження структури моделі. Внутрішня структура бази даних опису моделі
Генерація БД за моделлю Bold
Використання XML для зберігання даних
Використання middleware-сервера
Механізм підписок (Subscriptions)
Мова Object Constraint Language
Деякі особливості побудови інтерфейсу користувача засобами Bold
Використання стандартних дій (Actions) Bold
Рендерер (Renderers)
Обчислювані змінні в OCL виразах (Variables)
Висновок

1-а частина статті


Введення


У першій частині статті ми розглянули найпростіші приклади роботи з Bold. У даній статті ми продовжимо знайомство з Bold і розглянемо більш складні приклади.


Вивчення чого-небудь носить циклічний характер і просувається від простого до складного, дане твердження справедливо і для програмування. Тому ми вирішили написати другу частину статті в цьому ж ключі, побудувавши її у вигляді більш поглибленого вивчення складових Bold. Нагадаємо, що додаток Bold складається з трьох основних частин: користувальницького інтерфейсу, набору бізнес-правил (реалізованого у вигляді моделі) і шару, що відповідає за збереження моделі в будь-якому сховище даних. Послідовно поглибимо наші знання з кожної складової і почнемо з роботи з моделлю.


Ми злітаємо


Як приклад розглянемо додаток для оформлення замовлень на будь-які товари. Нижче (див. рисунок 1) приведена діаграма класів, що описує дану задачу.



Рисунок 1.


Тепер дамо короткі пояснення до діаграми класів:


Замовник описується класом Customer, який містить поля FirstName, MiddleName і LastName (ім’я, прізвище та по батькові замовника).


Опис замовлення відображено в класі Orders. OrderNo – номер замовлення, CreateDate – дата прийому замовлення.


Позиції замовлення описані класом OrderPositions. ItemCount – кількість одиниць товару в позиції, PositionNo – порядковий номер позиції, Price – ціна однієї одиниці товару.


Довідник номенклатури товарів – це клас Items. Він має лише одне поле Name – найменування товару.


Звичайно, модель далека від досконалості і для вирішення реальних завдань потрібні складніші моделі, але і ця модель дозволить нам розглянути ключові моменти роботи з Bold, не відволікаючись на зайві складності.


Збереження структури моделі. Внутрішня структура бази даних опису моделі


У першій частині статті в якості сховища даних моделі була обрана СУБД Interbase. Тепер ми розглянемо особливості використання інших СУБД і типів сховищ даних.


Як ми вже знаємо, діаграма класів через відповідну зв’язок (Link) імпортується у внутрішній формат Bold і відображається в редакторі компонентів BoldModel. Як і минулого разу, діаграма класів була створена в Model Maker. Ніяких особливостей порівняно з розібраним раніше прикладом немає, і тому ми не будемо докладно зупинятися на процесі імпорту моделі.


За збереження моделі в БД відповідає компонент TPersistenceHandleDB (що можна перевести як «диспетчер збереження» – persistence handler). Оскільки дослівний переклад на російську мову терміну «persistence handler »не відображає повністю його призначення, ми будемо використовувати оригінальний англомовний термін. Базовим класом для persistence handler є TPersistenceHandle. Bold дозволяє зберігати моделі в наступні типи сховищ:



В залежності від обраного типу сховища вибирається відповідний компонент-обробник (persistence handler).


Так, для СУБД – це TBoldPersistenceHandleDB, а для файлів XML – BoldPersistenceHandleFileXML.


Генерація БД за моделлю Bold


Коротко опишемо процес перетворення моделі в базу даних СУБД MS SQL Server.


На головну форму додатку помістимо компоненти BoldModel1 і BoldUMLMMLink1. Перший компонент, як ми вже знаємо, призначений для перегляду та редагування моделі, а другий дозволяє імпортувати модель з Model Maker. Властивість FileName компонента BoldUMLMMLink1 встановимо вказує на файл моделі Model Maker, а властивість BoldModel того ж компонента зробимо рівним BoldModel1. Двічі клацнемо мишкою на компоненті BoldModel1, при цьому відкриється редактор моделі. У формі редактора моделі натиснемо кнопку «import via link», щоб імпортувати модель з Model Maker.


Далі на форму помістимо компонент AdoConnection1, і налаштуємо його властивість Connection string на зв’язок з базою даних, в яку буде збережена модель. У нашому прикладі база розташована на локальному сервері і називається BoldAdv. Рядок з’єднання при цьому має наступний вигляд:





Provider=SQLOLEDB.1;Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=BOLDADV;Data Source=localhost

Потім помістимо на форму компонент BoldDatabaseAdapterADO1, відповідальний за запис моделі в базу через ADO. Його властивість Connection встановимо рівним ADOConnection1, а властивість DatabaseEngine – рівним dbeSQLServer. Властивість DatabaseEngine відповідає за правильну установку набору властивостей SQLDatabaseConfig відповідно до особливостей діалекту SQL сервера. У списку значень можливих властивостей доступно більшість сучасних серверів БД. Можна виставити властивості SQLDatabaseConfig і вручну, в таблиці наведено список найбільш часто використовуваних властивостей:

















































Властивість Опис
AllowMetadataChangesInTransaction: Boolean Виконувати зміни метаданих в транзакції. Дозволяє «обернути» зміна метаданих моделі в транзакцію, тим самим гарантуючи узгодженість метаданих моделі. Якщо сервер не підтримує транзакцію, то ця властивість необхідно встановити в false.
ColumnTypeFor <тип даних>: string SQL-ім’я типу даних типу даних.
DBGenerationMode: TBoldDatabaseGenerationMode Режим генерації бази: dbgTable – генерація з використанням компонентів TTable, рекомендується при генерації в формат локальних файл-серверних СУБД (Paradox) dbgQuery – генерація за допомогою TQuery і SQL – спосіб обраний за замовчуванням, як найбільш підходящий для серверів БД
DefaultStringLength: integer Довжина строкових полів за замовчуванням. Значення – 255. Необхідно, щоб задається довжина підтримувалася сервером.
Drop…Template: string; Шаблон SQL-вирази для видалення таблиці, стовпця або індексу (ALTER).
EmptyStringMarker: String Значення, що записується в якості порожній рядка.
Max…IdentifierLength: integer Максимальна довжина імен об’єктів БД.
QuoteNonStringDefaultValues: Boolean Укладати в лапки не рядкові значення за замовчуванням.
ReservedWords: TStringList Список зарезервованих слів діалекту SQL.
SQLforNotNull: string SQL-вираз для вибору не-NULL значень.
StoreEmptyStringsAsNULL Зберігати порожні рядки як NULL.
Supports…: Boolean; Підтримка в SQL діалекті того чи іншого SQL-виразів.
SystemTablePrefix: String; Префікс імен системних таблиць сервера.
UseSQL92Joins: boolean Підтримка сервером з’єднань таблиць в стилі SQL-92.SELECT FROM LEFT JOIN
ON WHERE
Таблиця 1

Останнім помістимо компонент BoldPersistenceHandleDB1 і встановимо його властивість BoldModel рівним BoldModel1, а властивість DatabaseAdapter – рівним BoldDatabaseAdapterADO1.


Далі знову перейдемо в редактор моделі, двічі клацнувши на BoldModel1, в редакторі натиснемо кнопку Generate database.






ПРИМІТКА

Крім вищеописаних дій, не забудьте згенерувати код (кнопка generate code) модулів BusinessClasses.inc, BusinessClasses.pas, BusinessClasses_Interface.inc.


Ось тепер саме час подивитися на структуру згенерованої бази.


Крім таблиць, відповідних класів нашої моделі, у базі даних зберігаються службові таблиці (див. таблицю 2).


































Таблиця Опис
BOLD_ID Таблиця містить останнє значення ідентифікатора створеного екземпляра об’єкта.
BOLD_TYPE Містить відповідність між рядковими іменами класів та їх номерами, використовуваними при збереженні об’єктів в базу
BOLD_TABLES Містить список таблиць, згенерованих Bold
BOLD_TIMESTAMP Містить кількість викликів методу UpdateDatabase, тобто номер останньої збереженої «версії» системи
BOLD_XFILES Кожен створений об’єкт потрапляє в цю таблицю і не видаляється навіть при видаленні об’єкта. Таблиця використовується для синхронізації із зовнішнім БД.
BOLD_MEMBERMAPPING, BOLD_R_CLSMAP, BOLD_W_CLSMAP Використовуються при еволюції моделі. Еволюція моделі буде розглянута пізніше.
BOLD_CLOCKLOG Зберігає інформацію про відповідність TimeStamp транзакції і реального часу. Властивість ClockLogGranularity дозволяє «прорідити» записи в даній таблиці при великому навантаженні.
BOLD_LASTCLOCK Використовується для зберігання останнього часу запису.
“BusinessClassesRoot” Зберігає інформацію про кореневому бізнес-класі моделі.
Таблиця 2

Використання XML для зберігання даних


Для зберігання даних в XML не потрібно попередньої підготовки, оскільки XML – досить гнучкий формат, що не вимагає наявності строго формалізованої структури. Зазвичай для опису структури XML-документа використовуються ті чи інші описи типу XSD, але розробники Bold вирішили, що моделі, що зберігається у внутрішньому форматі Bold, цілком достатньо. Мається на увазі, що доступ до XML буде вестися засобами Bold. Тому, на відміну від БД, де необхідно попередньо згенерувати структуру бази, у випадку з XML ніякої структури не генерується, а в XML-файл записуються безпосередньо дані, формат яких визначений в моделі.


Таким чином, в загальному випадку вся метаінформація моделі зберігається в компоненті TBoldModel.


За зберігання даних в форматі XML відповідає відповідний persistence handler – BoldPersistenceHandleFileXML.


Клас TBoldPersistenceHandleFileXML має такі властивості (таблиця 3):
















Властивість Опис
BoldModel:TBoldModel Посилання на зберігається модель
CacheData:Boolean Кешування інформації про об’єкти моделі. Якщо властивість встановлено в true, то дані про всі об’єкти зчитуються за один раз і зберігаються в пам’яті. Якщо ж значення дорівнює false, то зчитування даних, належать екземплярів об’єктів, проводиться лише за явної необхідності.
FileName:String Ім’я xml-файлу, в якому зберігається модель
Таблиця 3.

Перший сюрприз чекав нас при спробі завдання властивості FileName. Справа в тому, що діалог вибору імені файлу не дозволяє створити новий xml-файл. Спроба встановити вручну дане властивість таким чином, щоб воно вказувало на неіснуючий файл, тим не менш, виявляється успішною.


Відсутність доступного ззовні XSD ускладнює використання одержуваних XML-файлів в обхід Bold.


З яких причин реалізований саме такий алгоритм роботи з XML-файлом, поки так і залишилося для нас загадкою. Нижче наведено приклад XML-файла, отриманого після створення в системі примірників класів Customer і Items.





– <ValueSpace xmlns:xsi=”http://www.w3.org/1999/XMLSchema-instance”
xml:space=”preserve”>
– <Customer>
– <id xsi:type=”BoldDefaultObjectId”>
<ClassName>Customer</ClassName>
<DbValue>0</DbValue>
</id>
<persistencestate>0</persistencestate>
<existencestate>1</existencestate>
<timestamp>-1</timestamp>
– <members>
– <firstName>
<persistencestate>0</persistencestate>
<content>11</content>
</firstName>
– <lastName>
<persistencestate>0</persistencestate>
<content>444</content>
</lastName>
– <middleName>
<persistencestate>0</persistencestate>
<content>33</content>
</middleName>
– <owned_by_customer>
<persistencestate>0</persistencestate>
– <content>
<id xsi:null=”1″ />
<OrderNo>0</OrderNo>
</content>
</owned_by_customer>
</members>
</Customer>
– <Items>
– <id xsi:type=”BoldDefaultObjectId”>
<ClassName>Items</ClassName>
<DbValue>1849087502</DbValue>
</id>
<persistencestate>0</persistencestate>
<existencestate>1</existencestate>
<timestamp>-1</timestamp>
– <members>
– <name>
<persistencestate>0</persistencestate>
<content>good</content>
</name>
– <ordered_item>
<persistencestate>0</persistencestate>
– <content>
<id xsi:null=”1″ />
<OrderNo>0</OrderNo>
</content>
</ordered_item>
</members>
</Items>
</ValueSpace>

На жаль, ніякої документації про формат файлу ми знайти не змогли і знову ж базувалися на власному досвіді.


Файл містить набір тегів Customer та Item. Кількість і вид тегів відповідають кількості та типу створених примірників об’єктів того чи іншого класу. Усередині кожного такого тега присутній тег id, який описує тип унікального ідентифікатора об’єкта. Приклад:





<id xsi:type=”BoldDefaultObjectId”>

Тег ClassName описує тип створеного об’єкта





<ClassName>Items</ClassName>

Значення id, як нам здається, зберігається в теге DbValue





<DbValue>586093958</DbValue>

Наявність і призначення тегів persistencestate, existencestate і timestamp, що повторюються для кожного об’єкта з завидною постійністю, залишилося для нас загадкою. Ми, звичайно, здогадуємося, що тег persistencestate вказує на особливості збереження об’єкта, а existencestate – на його існування, але виявити ситуації, при яких значення цих тегів змінювалися, нам так і не вдалося.


Далі, тег містить в собі опис значень полів об’єкта. Наприклад, для об’єкта типу Customer:





 <members>
+ <firstName>
+ <lastName>
+ <middleName>
+ <owned_by_customer>
  </members>

Як видно з лістингу, крім полів простих типів, зберігаються і описані в моделі поля зв’язку примірника даного об’єкта з примірниками об’єктів інших класів.


Кожне з простих полів містить всього два тега: persistencestate і content.





– <firstName>
  <persistencestate>0</persistencestate>
  <content>a</content>
  </firstName>

Саме в поле content і зберігається поточне значення поля екземпляра об’єкта.


Використання middleware-сервера


Bold дозволяє не тільки зберігати дані безпосередньо в БД або XML, а й використовувати при цьому спеціальний middleware-сервер. Взаємодія між клієнтом і сервером може здійснюватися через HTTP, можливо, з використанням протоколу SOAP. Подивимося, як це робиться.


Наш сервер буде представляти собою ISAPI-додаток, написаний з використанням архітектури WebBroker. Використання WebBroker і формату ISAPI не є жорсткими вимогами і визначаються вашими вподобаннями.


Головними будівельними блоками є компоненти BoldHTTPServerPersistenceHandlePassthrough і BoldHTTPClientPersistenceHandle. Саме вони і забезпечують взаємодію між клієнтським додатком і middleware-сервером.


Створимо новий Web-додаток (File / New / Other -> Web Server Application). В WebModule помістимо компоненти:



  • BoldModel1 – Компонент, який зберігає метадані моделі.
  • BoldUMLMMLink1 – Зв’язок з файлом моделі, спроектованим в ModelMaker.
  • BoldPersistenceHandleDB1 – Persistence handle реляційної БД.
  • BoldDatabaseAdapterADO1 – Адаптер доступу до ADO.

ADOConnection1 – з’єднання з БД, в яку будуть зберігатися дані. Відразу хочу відзначити одну особливість. Так як додаток виконується під управлінням Web-сервера, то обліковий запис, від імені якої йтиме звернення до бази даних, не буде співпадати з тією, під якою ви працюєте в Windows. Цей факт треба враховувати при установці режиму аутентифікації в Windows. При використанні режиму аутентифікації c явно заданим паролем і логіном описана проблема не виникає.


Ми не будемо докладно розписувати, які властивості потрібно встановити у описаних вище компонентів, так як до цього моменту, сподіваюся, ви вже здатні це зробити самостійно.


Настав час розмістити в Web-модулі компонент BoldHTTPServerPersistenceHandlePassthrough і присвоїти йому ім’я httpMapper. Властивість BoldModel встановимо рівним BoldModel1, а властивість PersistenceHandle – рівним BoldPersistenceHandleDB1. Як ви вже здогадалися, властивість BoldModel задає модель, а PersistenceHandle – це компонент, що забезпечує обробку даних при збереження.


Залишилися останні штрихи, а саме написати пару обробників подій. Перший обробник WebModuleCreate викликається при створенні об’єкта Web-модуля. У ньому необхідно активізувати систему підтримки збереження, і складається він лише з одного рядка:





BoldPersistenceHandleDB1.Active:=true;

Другий обробник – WebModule1WebActionItem1Action – є дією за замовчуванням для Web-модуля. Щоб додати його, необхідно два рази клацнути мишкою на WebModule, і у вікні редактора додати нову дію (action). Властивість Default належить дії WebModule1WebActionItem1Action, встановимо в true, а властивість PathInfo зробимо рівним “/ Clients”. Нагадаю, що PathInfo визначає підрядок URL, розташовану після імені ISAPI dll. Власне кажучи, от і все, залишилося скомпілювати програми та помістити його на Web-сервер. Ми встановили його у віртуальну папку BoldClients.


Приступаємо до створення клієнта. На відміну від звичайного клієнта, в додатку, що використовує збереження даних через HTTP-сервер, обробник збереження є компонентом класу TBoldHTTPClientPersistenceHandle, що цілком логічно. З одного боку, у BoldHTTPClientPersistenceHandle є властивість BoldModel, яке повинно вказувати на модель програми. З іншого боку, у нього є властивість WebConnection, яке вказує на компонент класу TBoldWebConnection. Компонент TBoldWebConnection, дозволяє настроїти параметри підключення до HTTP-сервера. Нам необхідно зробити властивість URL BoldWebConnection рівним http://localhost/BoldClients/BoldServer.dll/Clients. Нагадаю, що BoldClients – це ім’я віртуальної папки, в якій лежить ISAPI dll сервера, Clients – це Path Info дії Web-модуля, що виконує збереження моделі, про призначення інших властивостей можна прочитати в документації. Ось і вся специфіка. Повний код додатків сервера і клієнта представлений в прикладах до статті.


Механізм підписок (Subscriptions)


Механізм підписки дозволяє одному компоненту отримувати повідомлення про події іншого. Компонент, який отримує повідомлення, називається передплатником (Subscriber). Він є спадкоємцем класу TBoldSubscriber. Компонент, що генерує повідомлення, називається видавцем (Publisher) і повинен бути спадкоємцем TBoldPublisher. Механізм підписок широко використовується у всіх частинах Bold. Існують два різновиди підписок:



  • Проста (Plain) – Видавець сповіщає передплатників про свої події. Передплатники отримують повідомлення.
  • Запит-відповідь (Query-Answer) – Видавець сповіщає передплатників і запитує дозвіл на вказане в повідомленні дію. Передплатник може накласти вето, тобто заборонити видавцеві виконання запитаного дії.

Події є екземпляри TBoldEvent, який є цілим числом типу integer.





type TBoldEvent = Integer;

Крім цього, події можуть бути екземплярами TBoldSmallEvent, який також є цілим числом, але має більш вузький діапазон значень (0 … 31), ніж TBoldEvent. Перевага використання TBoldSmallEvent полягає в тому, що в одній підписці можна отримувати повідомлення відразу про декілька «маленьких» події.


У модулі BoldSubscription.pas визначено ряд констант, які використовуються як внутрішніх подій Bold. Всі вони мають тип TBoldSmallEvent. Розробники можуть визначати свої події для власних потреб, при цьому не потрібно стежити за тим, щоб нові події не перекривали номери вже використовуються, так як передплатник і видавець можуть «домовитися» інтерпретувати приходять повідомлення за власним розсуд.


Базовими операціями при роботі з підпискою є оформлення передплати та отримання події передплати. Підписка проводиться за допомогою виклику методу видавця TBoldPublisher.AddSubscription або AddSmallSubscription.





procedure AddSubscription(Subscriber: TBoldSubscriber;
OriginalEvent: TBoldEvent; RequestedEvent: TBoldRequestedEvent);
procedure AddSmallSubscription(Subscriber: TBoldSubscriber;
Events: TBoldSmallEventSet; RequestedEvent: TBoldRequestedEvent
);

Перший параметр, Subscriber: TBoldSubscriber, вказує на підписувача


Другий параметр, OriginalEvent, визначає подія, на яку проводиться передплата. У разі використання «маленьких» подій (TBoldSmallEvent) можна підписатися на кілька подій одночасно.


Третій параметр, RequestedEvent: TBoldRequestedEvent – це подія, яка використовується при відповіді на вхідне подія в разі підписки типу запит-відповідь.


Видавець сповіщає передплатників про події подію за допомогою методів TBoldPublisher.SendExtendedEvent або TBoldPublisher.SendQuery.


Метод SendExtendedEvent застосовується для простих повідомлень. Він оголошений таким чином:





procedure SendExtendedEvent(Originator: TObject;
OriginalEvent: TBoldEvent; const Args: array of const);

Перший параметр, Originator: TObject, це об’єкт в якому відбулася подія.


Другий параметр, OriginalEvent: TBoldEvent, містить номер події.


Третій параметр містить додаткові параметри події.


Метод SendQuery призначений для відправки повідомлення запит-відповідь і оголошений так:





function SendQuery(Originator: TObject;
OriginalEvent: TBoldEvent;
const Args: array of const;
Subscriber: TBoldSubscriber): Boolean;

Метод в циклі пересилає повідомлення про подію передплатникам, і якщо всі передплатники підтверджують повідомлення, повертає True. Якщо ж хоча б один передплатник не згоден з повідомленням, цикл переривається і повертається False. Так як опитування передплатників ведеться до першого вето, то немає гарантії, що всі передплатники отримають запит на підтвердження повідомлення.


При оформленні підписки передплатник, як ви помітили, передає видавцеві параметр RequestEvent. Необхідність в цьому параметрі стане зрозуміла, якщо ми згадаємо, що номери подій не унікальні, і передплатник може оформити підписку у кількох різних видавців. Таким чином, нам необхідно мати якийсь ознака, що визначає контекст номера події. Нижче наведено приклад коду, що використовує описану концепцію.





const
breListBigger = 42
breNonBiggerChange = 43;
/ / Код для оформлення передплати, при оформленні передаються різні RequestEvent
List.AddSmallSubscription(
subscriber, [beItemAdded], breListBigger);
List.AddSmallSubscription(
subscriber,
[beItemDeleted, beItemReplaced, beOrderChanged],
breNonBiggerChange);
/ / Частина методу-передплатника, обробного події, що надсилаються видавцем
TMySubscriber.Receive(…; RequestedEvent: TBoldRequestedEvent);
begin / / Аналізуємо RequestEvent
case RequestedEvent of
breListBigger: ….

breNonBiggerChange: …
end;
end;

Безпосередньо механізм передплати не використовує RequestEvent і не звертає на цей параметр ніякої уваги. Як видно з прикладу, даний параметр використовується передплатником для вказівки контексту події.


На додаток до подій, розсилає видавцем за допомогою явних викликів SendExtendedEvent, перед руйнуванням примірника видавця всім передплатникам надсилається повідомлення beDestroying. Це робиться викликом методу TBoldPublisher.NotifySubscribersAndClearSubscriptions.


Класи, які хочуть виступати в ролі видавця, як правило, агрегує в собі клас TBoldPublisher, визначаючи публічні методи AddSubscription і AddSmallSubscriptions для оформлення передплати. Ніхто не забороняє визначити й інші публічні методи для підтримки підписки. Наприклад, клас TBoldDirectElement визначає метод DefaultSubscribe для оформлення найбільш поширених типів підписок. В ієрархії класів Bold визначені кілька готових класів, агрегується TBoldPublisher: TBoldSubscribableObject, TBoldSubscribablePersistent і TBoldSubscribableComponent, і використовуються як предків для передплатників.


При створенні власних класів видавців перед руйнуванням примірника об’єкта не забудьте викликати метод TBoldPublisher.NotifySubscribersAndClearSubscriptions. Це сповістить абонентів про припинення існування видавця.


Класи, які повинні виступати в ролі передплатника, можуть бути успадковані від класу TBoldSubscriber. При цьому повинні бути перевизначені методи Receive і Answer. Однак більш зручним і часто застосовуваним способом є використання класу – адаптера TBoldPassthroughSubscriber. Даний клас просто спрямовує отримані повідомлення вказаному обробникові, при цьому компонент, реально обробляє повідомлення, може не бути спадкоємцем TBoldSubscriber.


Мова Object Constraint Language


Мова OCL є мовою опису обмежень об’єктів і широко використовується в Bold для роботи з екземплярами об’єктів моделі.


Синтаксис мов ОСl і Object Pascal дуже схожий, тому в даній статті ми не будемо зупинятися на цьому питанні. Детальніше про OCL ви зможете дізнатися на сайті rsdn.ru.


Деякі особливості побудови інтерфейсу користувача засобами Bold


У даному розділі ми зібрали опис деяких типових прийомів побудови GUI з використанням Bold.


Використання стандартних дій (Actions) Bold


При установці Bold додає в список стандартних дій (Actions) дії, специфічні для Bold. Нижче наведено список найбільш часто використовуваних дій Bold з коротким описом.



















Дія Опис
TBoldActivateSystemAction Активізація системи.
TBoldUpdateAction Збереження зроблених змін та внесених даних.
TBoldGenerateSchemaAction Генерація пустої бази даних, відповідної моделі.
TBoldUndoAction Скасовує зміни, зроблені після установки останньої контрольної точки (checkpoint).

Тепер замість написання коду обробників подій елементів управління, що відповідають за створення, відкриття бази, відкат і збереження введених даних, ми можемо присвоїти елементам управління стандартні дії Bold, тим самим ще більше полегшивши нашу працю. Приклад використання дій Bold можна побачити в прикладі, що додається до статті.


Рендерер (Renderers)


Багато візуальні компоненти для побудови GUI мають можливість зміни свого зовнішнього вигляду в залежності від деяких умов. Одним з прикладів таких компонентів є компонент TBoldGrid. Стандартним прийомом зміни зовнішнього вигляду осередків в залежності від представлених в них даних являетcя написання обробників подій OnDraw. Наприклад, для «розцвічування» осередків стандартного компонента TDBGrid використовується обробник повідомлення OnDrawColumnCell. Крім цього способу Bold надає набагато більш зручний і гнучкий механізм, який отримав назву rendering. Далі ми будемо використовувати для його позначення русифікований термін – рендеринг. Головна ідея рендеринга полягає в тому, що візуальний компонент передає функції формування параметрів відтворення своїх даних спеціальному невізуальному компоненту – рендерер (Renderer). Базовим класом для компонентів-рендерер є TBoldRenderer, спадкоємці якого дозволяють вирішити типові завдання відтворення даних. Застосування рендерер має ряд переваг:


Різні візуальні компоненти можуть використовувати один рендерер для відтворення своїх даних. Це позбавляє від необхідності узгодження набору параметрів обробників подій для різних контролів, що виникає при використанні стандартного механізму.


У спадкоємця TBoldRenderer можна зосередити код, який обчислює параметри відтворення (наприклад, встановити жирний шрифт в залежності від значення рядка).


Один і той же рендерер може реагувати на зміни даних, вироблені в різних control-ах.


Сподіваюся, що ми переконали вас у перевагах використання рендеринга, і тепер перейдемо до безпосереднього розгляду цього механізму.


Як приклад розглянемо наступну задачу: Клас Customer нашої моделі містить атрибути FirstName, MiddleName і LastName (ім’я, по батькові та прізвище замовника). Необхідно при відображенні і редагуванні даних атрибутів в сітці TBoldGrid виводити в окремому стовпці строчку з повними даними про замовника (об’єднання рядків) і забезпечити контроль повноти введення даних. Ми не будемо описувати детально створення заготовки програми (так як робили це вже раніше), відзначимо лише, що крім інших компонентів помістили на форму компонент-рендерер BoldAsStringRenderer1 класу TBoldAsStringRenderer, т.е рядковий рендерер. Нижче наведено зовнішній вигляд отриманої форми (див. малюнок:



Рисунок 2а.


При використанні рендерера необхідно написати код обробників його подій. Першим виникає питання: яким чином рендерер буде дізнаватися про те, що користувач вводить або змінює дані? Для цього використовуються передплати. В обробнику події OnSubscribe нашого рендерера ми повинні підписатися на зміни полів FirstName, MiddleName і LastName об’єктів Customer, код обробника виглядає наступним чином:





procedure TForm1.BoldAsStringRenderer1Subscribe(Element: TBoldElement;
Representation: Integer; Expression: String;
Subscriber: TBoldSubscriber);
begin
with Element as TCustomer do
begin
SubscribeToExpression(“firstName”, Subscriber);
SubscribeToExpression(“middleName”, Subscriber);
SubscribeToExpression(“lastName”, Subscriber);
end;
end;

Подія OnSubscribe виникає при ініціалізації системи підписок. Параметр Element містить клас власника передплати. У першому рядку обробника ми перевіряємо, що даний клас є класом TCustomer, і якщо це так, то підписуємося на отримання повідомлень про зміну полів FirstName, MiddleName і LastName. Підписка здійснюється викликом методу SubscribeToExpression. Перший параметр – ім’я OCL-вирази, на зміни, якого ми підписуємося, другий – компонент-передплатник. Параметр Subscriber вказує на внутрішній об’єкт-передплатник рендерера. Тепер наш рендерер буде реагувати на зміни даних про замовника.


Далі потрібно забезпечити формування значення ПІБ замовника. Це робиться в обробнику події OnGetAsString рендерера.





function TForm1.BoldAsStringRenderer1GetAsString(Element: TBoldElement;
Representation: Integer; Expression: String): String;
begin
Result := “”;
if Assigned(Element) then
begin
with Element as TCustomer
do
begin
if FirstName <> “” then
Result := FirstName
else Result: = “<Ім'я>“;
if MiddleName <> “” then
Result := Result + ” ” + MiddleName
else Result: = Result + “<батькові>“;
if LastName <> “” then
Result := Result + ” ” + LastName
else Result: = Result + “<Прізвище>“;
end;
end;
end;

Оброблювач повертає рядок ПІБ, «склеюючи» атрибути FirstName, MiddleName і LastName в один рядок. Якщо який-небудь з атрибутів порожній, то в якості його значення вставляється рядок <...>.


Наступний обробник рендерера OnSetColor відповідає за обчислення кольору рядки, що формується рендерер:





procedure TForm1.BoldAsStringRenderer1SetColor(Element: TBoldElement;
var AColor: TColor; Representation: Integer; Expression: String);
begin
with Element as TCustomer do
begin
if (FirstName = “”) or (MiddleName = “”) or (LastName = “”) then
AColor:=clRed
else
AColor:=clSilver;
end;
end;

Якщо не введені ім’я, прізвище або по батькові, колір буде червоним, якщо ж все заповнено – сірим.


Таким чином, ми забезпечили відображення потрібної нам інформації. Але найцікавіше те, що ми можемо досить легко запрограмувати можливість зміни поля ПІБ таким чином, щоб при введенні в нього даних змінювалися значення окремих полів імені, прізвища та по батькові. Для цього нам треба написати два обробника подій рендерера: OnMayModify і OnSetAsString.


Оброблювач OnMayModify дозволяє перевірити можливість редагування поле ПІБ:





function TForm1.BoldAsStringRenderer1MayModify(Element: TBoldElement;
Representation: Integer; Expression: String;
Subscriber: TBoldSubscriber): Boolean;
begin
with Element as TCustomer do
begin
result := EvaluateExpressionAsDirectElement(
“firstName”).ObserverMayModify(Subscriber)
and EvaluateExpressionAsDirectElement(
“middleName”).ObserverMayModify( Subscriber )
and EvaluateExpressionAsDirectElement(
“lastName”).ObserverMayModify( Subscriber );
end;
end;

Код обробника зрозумілий, і ми не будемо його коментувати, щоб не відволікати увагу на поки неважливі деталі.


Другий обробник OnSetAsString викликається при спробі редагування поля ПІБ.





procedure TForm1.BoldAsStringRenderer1SetAsString(Element: TBoldElement;
NewValue: String;
Representation: Integer;
Expression: String);
begin
with Element as TCustomer do
begin
FirstName := Copy(NewValue, 0, (Pos(” “, NewValue) -1));
NewValue:=Copy(NewValue,
(Pos(FirstName, NewValue) + Length(FirstName)+1),
Length(NewValue) – Length(FirstName));
MiddleName:=Copy(NewValue, 0, (Pos(” “, NewValue) -1));
NewValue:=Copy(NewValue,
(Pos(MiddleName, NewValue) + Length(MiddleName)+1),
Length(NewValue) – Length(MiddleName));
LastName:=NewValue;
end;
end;

Даний обробник розбирає введену користувачем рядок з ПІБ на окремі складові – прізвище, ім’я та по батькові. Ми вважаємо, що окремі складові ПІБ розділяються пробілами. За результатами розбору ми обчислюємо значення прізвища, імені та по батькові.


Останнє, що необхідно зробити – це створити в сітці TBoldGrid новий стовпець для відображення / редагування ПІБ, і його властивості Renderer встановити значення BoldAsStringRenderer1. Результат наших праць представлений на малюнку 2.



Рисунок 2б.


Обчислювані змінні в OCL виразах (Variables)


Реалізація OCL в Bold дозволяє використовувати обчислювані змінні всередині OCL-виразів. Обчислення значень змінних проводиться в коді програми. Використання обчислюваних змінних надає OCL-виразами додаткову гнучкість.


На головну форму додатку помістимо рядок введення для вказівки умов пошуку та кнопку для початку пошуку. Там же розмістимо компонент-грід (TBoldGrid) для відображення результатів пошуку. Зовнішній вигляд частини форми наведено на малюнку 3.



Рисунок 3.


Далі на форму помістимо компонент bhlFind: TBoldListHandle, у властивості Expression якого напишемо OCL-вираз «Customer.allInstances-> select (lastName.regExpMatch (var_string))». Цей вираз вибирає всі екземпляри замовників, що мають у прізвища рядок, збігається зі значенням змінної var_string. Щоб знайдені значення відображалися в гріді, властивість BoldHandle компонента BoldGrid2 встановимо рівним bhlFind.


Тепер нам належить головне – встановлювати значення змінної var_string відповідно з рядком введеної користувачем. Першим кроком до вирішення цього завдання є визначення OCL-змінної var_string. Справді, звідки система дізнається, що така мінлива є? Список користувальницьких змінних зберігається в компоненті BoldOclVariables1: TBoldOclVariables. Цей компонент із закладки BoldHandles необхідно помістити на форму. У властивості-колекції Variables необхідно визначити змінну var_string.


Як же організувати обчислення значення змінної? Для організації зв’язку OCL-змінна – код існує спеціальний компонент TBoldVariableHandle. Даний компонент знаходиться на закладці BoldHandle. Помістимо даний компонент на форму і назвемо його varSearchString. Властивість BoldHandle класу-змінної var_string встановимо рівним varSearchString. Далі налаштуємо властивості varSearchString, як показано в таблиці (Див. таблицю 4):













Властивість Значення
StaticSystemHandle BoldSystemHandle1
ValueTypeName String
Таблиця 4

Таким чином, використовуючи компонент varSearchString: TBoldVariableHandle ми можемо в коді встановлювати значення OCL-змінної var_string. У нашому випадку ми робимо це в обробнику події натискання на кнопку пошуку.





procedure TForm1.Button3Click(Sender: TObject);
begin
varSearchString.Value.AsString:=edSearchString.Text;
end;

Залишилося тільки скомпілювати і запустити додаток.


Висновок


Ми сподіваємося, що прочитання статті переконає вас у тому, що технологія Bold для Delphi виводить розробників на абсолютно новий рівень побудови інформаційних систем.


Ми розглянули рішення кількох досить непростих задач, а також розглянули відповідні VCL-компоненти. І, хоча цю статтю не можна назвати посібником з Bold, вона може служити опорною точкою при вивченні цієї дуже потужної технології від фірми Borland.

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*