Матрёшка: второе поколение реализации шаблона «визитёр» для обхода элементов моделей

В статье Матрёшка: использование шаблона «визитёр» для обхода элементов UML модели мы уже рассматривали использование шаблона «визитёр» для обхода элементов UML модели с целью генерации кода. Поскольку с тех времён много воды утекло, да и в Матрёшке появилась совершенно иная реализация шаблона «визитёр», пришло время ещё раз обратить внимание на особенности использования новой реализации шаблона «визитёр».

Новая реализация подразумевает разделение ответственности на два компонента — обработчик и обходчик. Обработчик реализуется пользователем и предназначен для выполнения полезной работы при обходе элементов; а обходчик реализует необходимый алгоритм обхода элементов. Другой важной особенностью новой реализации является возможность обработки элементов разных метамоделей в рамках одного обработчика, что часто бывает необходимым в частности для работы с моделями, использующими UML профили.

Обработчик или Visitor

Обработчик элементов конкретной метамодели объявляется в пакете AMF.Visitors.<Metamodel>_Visitors в виде интерфейсного типа, который содержит по две операции для каждого не абстрактного класса метамодели со следующим профилем (на примере класса Class метамодели UML):


          

          
   not overriding Enter_Class
    (Self    : in out UML_Visitor;
     Element : not null AMF.UML.Classes.UML_Class_Access;
     Control : Traversal_Control) is null;

   not overriding Leave_Class
    (Self    : in out UML_Visitor;
     Element : not null AMF.UML.Classes.UML_Class_Access;
     Control : Traversal_Control) is null;

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

Обходчик или Iterator

Ситуация с обработчиками выглядит в некоторой степени аналогично, для каждой метамодели объявлен интерфейсный тип обходчика в пакете AMF.Visitors.<Metamodel>_Iterators. Этот интерфейсный тип имеет по одной операции для обхода элементов неабстрактных классов метамодели. Например:


          

          
   not overriding Visit_Class
    (Self    : in out UML_Iterator;
     Visitor : in out AMF.Visitors.Abstract_Visitor'Class;
     Element : not null AMF.UML.Classes.UML_Class_Access;
     Control : Traversal_Control) is null;

Приложение конечно же может определить собственный обходчик, но это зачастую не требуется, поскольку Матрёшка предоставляет компоненты для конструирования обходчика элементов на основе принципа владения. Базовый компонент объявлен в пакете AMF.Visitors.Containment, и содержит объявление интерфейсного типа и реализацию подпрограммы инициации обхода корневых элементов экстента. Этот тип может использоваться с любыми метамоделями.

Для обхода элементов конкретной метамодели предоставляется настраиваемый пакет AMF.Visitors.Generic_<Metamodel>_Containment, реализующий специфический для метамодели алгоритм обхода элементов по принципу владения. Настраиваемые пакеты позволяют создавать обходчики для любого количества метамоделей путём выстраивания настроек в цепочку. Пример настройки обходчика для элементов только метамодели UML так же входит в состав Матрёшки и расположен в пакете AMF.Visitors.UML_Containment, имеющим следующее содержание:


          

          
with AMF.Visitors.Containment;
with AMF.Visitors.Generic_UML_Containment;

package AMF.Visitors.UML_Containment is
  new AMF.Visitors.Generic_UML_Containment
       (AMF.Visitors.Containment.Containment_Iterator);

Как же этим пользоваться

Небольшой пример использования новой реализации шаблона «визитёр». Предположим, имеется некоторый генератор кода, реализованный как обработчик элементов модели со следующей спецификацией:


          

          
   type Generator is
     limited new AMF.Visitors.UML_Visitors.UML_Visitor
       with null record;

   overriding procedure Enter_Class
    (Self    : in out Transformer;
     Element : not null AMF.UML.Classes.UML_Class_Access;
     Control : in out AMF.Visitors.Traverse_Control);

Для его исполнения достаточно написать:


          

          
procedure Generate (Extent : not null AMF.Extents.Extent_Access) is
   Iterator  : AMF.Visitors.UML_Containment.UML_Containment_Iterator;
   Generator : Generators.Generator;

begin
   Iterator.Visit (Generator, Extent);
end;

Автор: Вадим Годунко
Дата: 11.03.2012