Мистецтво метапрограмування: Частина 3. Корпоративне метапрограмування (исходники), Різне, Програмування, статті

“О, ні, – скажете ви, – тільки не ще одна” теоретична “дискусія!” Але, тим не менш, прочитайте статтю до кінця – ці міркування про теорію метапрограмування можуть допомогти вам відокремити факти від вигадок, які часом зустрічаються в бурхливих маркетингових заявах розробників різних інструментів метапрограмування.


У цій статті досліджуються теоретичні рамки можливостей метапрограмування та архітектури OMG “s MDA. Ця архітектура відділяє бізнес-логіку і логіку додатків від лежачої в їх основі технологічної платформи. Дізнайтеся про проблеми, які можна вирішувати з допомогою MDA, і розгляньте приклад текстової системи, що використовує цю архітектуру.


Теоретичні рамки можливостей метапрограмування


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


Графічне і текстове метапрограмування


В перший і друга статтях цієї серії ми розглядали текстові системи, тим не менш, багато існуючі системи метапрограмування є графічними. Давайте почнемо з розгляду програми, представленої як в текстовому, так і в графічному вигляді, і з’ясуємо, які існують подібності та відмінності. У цьому прикладі використовуються регулярні вирази.


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


У лістингу 1 міститься регулярний вираз на мові Perl, яке замінює кожне входження підрядка <h1> значенням <h2> в рядку HTML-коду:


Лістинг 1. Регулярний вираз Perl для заміни HTML-тегів





$html_str =~ s!<(/?)h1>!<${1}h2>!g;

Мова, на якому написано регулярний вираз, не є мовою Perl; це предметно-орієнтований мову, яка використовується Perl. Як би там не було, точний синтаксис регулярного виразу не так важливий – регулярні вирази можуть бути складені багатьма різними способами.


Як правило, коли ви спілкуєтеся з комп’ютером на повнофункціональному або на вузькоспеціалізованому мові програмування, ви використовуєте певний спосіб передачі символів цієї мови компілятору або інтерпретатору. Ви можете робити це, просто набираючи код, але також ви могли б висловити вищезазначене регулярний вираз наступним чином:


Рисунок 1. Регулярний вираз Perl у вигляді діаграми

При збереженні діаграми переконайтеся, що прапорець “compress diagram files” знято.


Короткий огляд формату файлу програми Dia


Файл програми Dia є простим файлом формату XML, що використовують власні схему і простір імен Dia. Цей файл може бути як стислим, так і стислому (наша програма має справу з тільки з незжатими файлами). Формат файла досить простий. Нижче наведені деякі фрагменти файлу, який ми тільки що створили.


Лістинг 3. Приклад файлу Dia





<?xml version=”1.0″ encoding=”UTF-8″?>
<dia:diagram xmlns:dia=”http://www.lysator.liu.se/~alla/dia/”>
<dia:diagramdata>
<dia:attribute name=”background”>
<dia:color val=”#ffffff”/>
</dia:attribute>


</dia:diagramdata>
<dia:layer name=”Background” visible=”true”>
<dia:object type=”UML – Class” version=”0″ id=”O0″>
<dia:attribute name=”obj_pos”>
<dia:point val=”11.6,6.35″/>
</dia:attribute>


<dia:attribute name=”name”>
<dia:string>#Person#</dia:string>
</dia:attribute>

<dia:attribute name=”attributes”>
<dia:composite type=”umlattribute”>
<dia:attribute name=”name”>
<dia:string>#id#</dia:string>
</dia:attribute>
<dia:attribute name=”type”>
<dia:string>#int#</dia:string>
</dia:attribute>
<dia:attribute name=”value”>
<dia:string>##</dia:string>
</dia:attribute>


</dia:composite>
</dia:attribute>
<dia:attribute name=”operations”/>
<dia:attribute name=”template”>
<dia:boolean val=”false”/>
</dia:attribute>
<dia:attribute name=”templates”/>
</dia:object>
</dia:layer>
</dia:diagram>

У файлі міститься багато інформації, але тільки мала її частина є для нас корисною. Зверніть увагу на блоки XML, що починаються з тегів, подібних тегу <dia:object type=”UML – Class” version=”0″ id=”O0″>. Далі, зверніть увагу на ім’я класу, а також на ім’я і тип кожного атрибута.


Ім’я класу міститься в атрибуті з ім’ям name. Всі атрибути містяться в гілці з назвою attributes, кожен атрибут є атрибутом класу umlatrribute з відповідними полями name і type. За Через це серед величезної кількості тегів XML зосередьте увагу тільки на тих з них, які представляють інтерес.


Реалізація MDA


Реалізація MDA – це простий парсер SAX, написаний на мові Scheme з використанням модуля SSAX. Парсер зчитує дані з одного файлу з розширенням. Dia і записує дані у файл SQL, а також у вихідний файл C + +. Для кожного класу створюється клас C + + і таблиця бази даних.


Для максимального спрощення в нашому прикладі підтримуються тільки атрибути (не операції), і не реалізовані функції завантаження / збереження бази даних. Тим не менш, для додавання цього функціоналу потрібно виконати лише кілька додаткових кроків, додавши їх до парсеру, наведеному в лістингу 4.


Нижче наведено код програми MDA, написаний для використання з компілятором Chicken Scheme (інші схеми, ймовірно, зажадають лише незначної модифікації в рядку завантаження модуля SSAX).


Лістинг 4. Повний лістинг програми для програми MDA




                 ;; Завантаження парсера Scheme SAX
(require-extension ssax)
;; Строкові значення Dia укладені в символи “#”. Наступний ;; Блок видаляє ці символи.
(define (munge-dia-value val)
(let (
(str-len (string-length val)))
(if (<= str-len 2)
“”
(substring val 1 (- str-len 1)))))
;; Функція парсера SAX
(define (dia-mda-parser input-port cpp-out-port sql-out-port)
(let ( ;; Змінні стану – можливо, буде більш правильним ;; Зберігати їх разом з seed, але якщо помістити їх ;; Сюди, буде легше керувати ними
; Це використовується для правильної обробки списків, розділених комами
(first #t) ; Наступні змінні використовуються для зберігання типу і імені атрибута, ; Поки вони не будуть лічені для роздруківки
(attr-type “”)
(attr-name “”))
;; Пишемо заголовок C + +
(display “#include <string>” cpp-out-port)(newline cpp-out-port)
(display “using namespace std;” cpp-out-port)(newline cpp-out-port)
(display “typedef string text;” cpp-out-port)(newline cpp-out-port)
;; Макрос SSAX для створення парсера
((SSAX:make-parser
;; Функція для роботи з елементом
NEW-LEVEL-SEED
(lambda (elem-gi attrs namespaces expected-content seed) ;; Ім’я елемента – це cdr списку імен
(define element (cdr elem-gi)) ;; Загальні атрибути
(define name-attr (assq “name attrs))
(define type-attr (assq “type attrs))
;; Перемикач стану – визначаємо, в якому елементі і в якому стані ;; Ми знаходимося, а також стан, в яке нам ;; Належить переключитися (якщо таке є)
;; ;; Зверніть увагу, що повертається значення цієї (та інших) функцій ;; Стає новим значенням “seed”, тому ми використовуємо його для запису ;; Поточного стану. Додаткову інформацію про внутрішні ;; Обробках SSAX ви можете отримати в розділі посилань.
(cond ; Перевірка з метою почати клас вищого рівня
((and (eq? seed “top) (eq? element “object) (equal? (cdr type-attr) “UML – Class”))
(set! first #t) ;reset the “comma” state
“object) ; Перевірка того, чи проводиться пошук імені класу
((and (eq? seed “object) (eq? element “attribute) (equal? (cdr name-attr) “name”))
“class-name) ; Перевірка того, чи готові ми прочитати ім’я класу
((and (eq? seed “class-name) (eq? element “string))
“read-class-name) ; Перевірка для з’ясування того, перемикаємося ми в режим атрибута
((and (eq? seed “object) (eq? element “attribute) (equal? (cdr name-attr)
“attributes”)) “class-attrs) ; Перевірка для з’ясування того, чи почали ми пошук інформації про атрибуті
((and (eq? seed “class-attrs) (eq? element “composite) (equal? (cdr type-attr)
“umlattribute”)) “class-attribute) ; Перевірка того, шукаємо ми ім’я атрибута
((and (eq? seed “class-attribute) (eq? element “attribute) (equal? (cdr name-attr)
“name”)) “class-attribute-name) ; Перевірка того, зчитуємо ми ім’я атрибута
((and (eq? seed “class-attribute-name) (eq? element “string))
“read-class-attribute-name) ; Перевірка того, шукаємо ми тип атрибута
((and (eq? seed “class-attribute) (eq? element “attribute) (equal? (cdr name-attr)
“type”)) “class-attribute-type) ; Перевірка того, зчитуємо ми тип атрибута
((and (eq? seed “class-attribute-type) (eq? element “string))
“read-class-attribute-type)
(else seed)))
; Завершальна функція роботи з елементом
FINISH-ELEMENT
(lambda (elem-gi attrs namespaces parent-seed seed) ;; Часто використовувані значення
(define element (cdr elem-gi))
(define type-attr (assq “type attrs))
(cond ; Після формування елемента umlatrribute випишемо інформацію про атрибуті
((and (eq? element “composite) (equal? (cdr type-attr) “umlattribute”))
(if first
(set! first #f)
(display “, ” sql-out-port))
(display (string-append attr-name ” ” attr-type) sql-out-port)
(display (string-append ” ” attr-type ” ” attr-name “;”) cpp-out-port)
(newline cpp-out-port)) ; Після формування елемента об’єкта UML-класу запишемо завершальні символи
((and (eq? element “object) (equal? (cdr type-attr) “UML – Class”))
(display “);” sql-out-port)(newline sql-out-port)
(display “};” cpp-out-port)(newline cpp-out-port))
(else #f))
; Відновимо відомості про батьківський рівні
parent-seed)
;; Функція символьних даних
CHAR-DATA-HANDLER
(lambda (s1 s2 seed)
(cond ; Прочитуємо ім’я класу і виписуємо відповідні оператори
((eq? seed “read-class-name)
(display “class ” cpp-out-port)
(display (munge-dia-value s1) cpp-out-port)
(display ” {” cpp-out-port) (newline cpp-out-port)
(display “public:” cpp-out-port) (newline cpp-out-port)
(display “create table ” sql-out-port)
(display (munge-dia-value s1) sql-out-port)
(display ” (” sql-out-port)) ; Прочитуємо і зберігаємо ім’я атрибута
((eq? seed “read-class-attribute-name)
(set! attr-name (munge-dia-value s1))) ; Прочитуємо і зберігаємо тип атрибута
((eq? seed “read-class-attribute-type)
(set! attr-type (munge-dia-value s1)))
(else #f))
seed)
) input-port “top)))
;;; Основна програма;;;
(let ( ;; Відкриття файлів;;
(tables-in (open-input-file “tables.dia”))
(cpp-out (open-output-file “tables.cpp”))
(sql-out (open-output-file “tables.sql”)))
;; Обробка даних;;
(dia-mda-parser tables-in cpp-out sql-out)
;; Закриття файлів;;
(close-input-port tables-in)
(close-output-port cpp-out)
(close-output-port sql-out))

Для простоти імена вхідного і вихідних файлів жорстко запрограмовані, тому переконайтеся, що ви зберігаєте файл програми Dia в ту ж папку, що й файл tables.dia. Збережіть програму під ім’ям mda.scm. Для її запуску просто виконайте команду csi mda.scm. На основі даних Dia ця програма створить два вихідних файлу. Вихідні дані SQL виглядають наступним чином:


Лістинг 5. Вихідні дані SQL, отримані в результаті виконання програми MDA





create table Person (id int, name text, address text);

Вихідні дані C + + виглядають наступним чином:


Лістинг 6. Вихідні дані C + +, отримані в результаті виконання програми MDA





#include <string>
using namespace std;
typedef string text;
class Person {
public:
int id;
text name;
text address;
};

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


Висновок


Model Driven Architecture є потужною концепцією, заснованої на міцних традиціях метапрограмування. Використовуючи ідею платформо-незалежних моделей, MDA поки що виходить за рамки традиційних методів.


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


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

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

Ваш отзыв

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

*

*