Pzl-Компонента

Материал из wikiru.visual-prolog.com

Автор: Виктор Юхтенко

Необходимые условия признания класса pzl-компонентой

Любой класс, порождающий объекты (имеющий интерфейс), может быть признан компонентой, если

  1. Имя интерфейса и имя класса различаются;
  2. Интерфейс класса поддерживает интерфейс pzlComponent;
  3. Интерфейс класса включает декларации констант:
    1. componentID_C с типом pzlDomains::entityUID_D
    2. componentAlias_C с типом string
    3. componentRunAble_C с типом core::booleanInt
    4. componentMetaInfo_C с типом core::namedValue*.
    5. сomponentDescriptor_C с типом pzlDomains::pzlComponentInfo_D;
  4. Декларация класса содержит единственный конструктор new(object ИспользуемыйОбъект);
  5. Имплементация класса наследует от класса pzlComponent;
  6. Пакетный файл деклараций класса (.PH) включает файл "System\Packs\pzlcomponent\pzlcomponent.ph".

Ниже приведены фрагменты различных составляющих компоненты, удовлетворяющей перечисленным выше условиям.

Декларация класса

Содержит единственный коструктор new:(object UsingObject). Имя интерфейса отличается от имени класса.
class сomponentExample:iComponentExample
 
constructors
  new:(object UsingObject).
 
end class

Интерфейс класса

Cодержит квалификатор supports pzlComponent и декларацию констант
  • componentID_C
  • componentAlias_C
  • componentRunAble_C=b_True
  • componentMetaInfo_C
  • componentDescriptor_C
interface iComponentExample
  supports pzlComponent
 
open core
constants
  componentDescriptor_C:pzlDomains::pzlComponentInfo_D=pzlDomains::pzlComponentInfo
    (
    componentAlias_C,
    componentID_C,
    componentRunAble_C,
    componentMetaInfo_C
    ).
  componentID_C:pzlDomains::entityUID_D=pzlDomains::str("ComponentExample").
  componentAlias_C="ПримерКомпоненты".
  componentRunAble_C=b_True.
  componentMetaInfo_C:namedValue_List=[].
 
predicates
  show:().
 
end interface iComponentExample

Имплементация класса

Содержит директиву наследования inherits pzlComponent и содержит клаузу для конструктора new(UsingObject)
implement componentExample
    inherits pzlComponent
 
clauses
  new(UsingObject):-
    ...
 
clauses
  show():-
  ...
end implement componentExample

Файл деклараций пакета ComponentExample.PH

Содержит директиву включения файла PzlComponent.ph
#requires ...
#include @"System\Packs\pzlcomponent\pzlcomponent.ph"
 
#include @"pfc\core.ph"

Интерфейс pzlComponent

Интерфейс pzlComponent соответствует наследуемому pzl-Компонентой классу pzlComponent и делает класс pzl-компонентой, связывая ее с другими частями pzl-системы. Кроме того, этот интерфейс позволяет получать часть информации о компоненте.

В частности следующие предикаты поддерживаются содержательно pzl-системой

  • getContainerVersion() - возвращает информацию о версии контейнера, в котором данная компонента содержится
  • getContainerName() - возвращает информацию об имени файла-контейнера, в котором содержится данная компонента
  • getClassInfo(className,classVersion) - возвращает информации об имени и версии класса pzlComponent

Остальные предикаты интерфейса:

  • getComponentVersion()-> string
  • getComponentID() -> entityUID_D
  • getComponentAlias() -> string
  • getComponentMetaInfo() -> namedValue*
  • release

должны быть определены пользователем в имплементации базового класса компоненты. В противном случае генерируют исключения при обращении.

Предопределенные константы интерфейса pzl-компоненты

Константа componentID_С

Константа сomponentID_С определяет идентификатор компоненты, который (по-возможности) должен быть уникальным в глобальном смысле. То есть две программы в глобальном информационном пространстве (не говоря уже об одном копьютере), использующие компоненты с одним и тем же идентификатором могут быть уверены, что они используют точно одну и ту же pzl-компоненту. Этого можно добиться, если использовать способ, аналогичный способу, применяемому Microsoft для идентификации COM-компонент. Для этого в pzl-технологии используется структура с функтором uid:

uid(
  core::unsigned32 Unsigned,
  core::unsigned16 Short1,
  core::unsigned16 Short2,
  core::unsigned8 Byte1,
  core::unsigned8 Byte2,
  core::unsigned8 Byte3,
  core::unsigned8 Byte4,
  core::unsigned8 Byte5,
  core::unsigned8 Byte6,
  core::unsigned8 Byte7,
  core::unsigned8 Byte8).

что, на самом деле является полным аналогом структуры core::nativeGuid системы Visual Prolog. Идентификатор ComponentID используется в двух случаях:

  • Он является идентификатором, под которым в реестре Windows или в файле регистрации приложения записывается регистрационная информация о компоненте
  • Он является идентификатором, под которым эта компонента зарегистрирована в pzl-контейнере

Поскольку идентификатор в форме uid является громоздкой последовательностью цифр и букв, то применять его в процессе освоения технологии неудобно. С целью упрощения процесса освоения имеется вторая версия представления идентификатора - строковая - в форме str(string). Естественно, уникальность строкового представления обеспечить крайне трудно, но в процессе освоения, можно пожертвовать глобальной уникальностью и контролировать только уникальность идентификатора на данном компьютере. В результате мы получаем две альтернативные формы представления идентификатора в объявлении домена entityUID_D:

entityUID_D=
  str(string);
  uid(unsined16,...).

Константа componentAlias_C (Условное имя)

Наряду с уникальным идентификатором, pzl-технология предоставляет воможность присвоить каждой компоненте условное, содержательное имя, являющееся строкой символов. Для этого выбранное имя должно быть присвоено константе ComponentAlias_C. Однако уникальность Условного Имени (Alias) не является жестким требованием.

Так, Вы можете создать несколько текстовых редакторов с различными свойствами, которые поддерживают одни и те же интерфейсы. Каждый из этих текстовых редакторов будет иметь уникальный идентификатор типа entityUID_D и, возможно каждый из них будет иметь свое имя базового класса. Но все они могут иметь одно и то же условное имя "Текстовый Редактор". Тогда Ваша программа (подчеркнем, не зная имени класса), может при создании экземпляра pzl-компоненты использовать имя "Текстовый Редактор" и будет создаваться экземпляр той компоненты, которая в данный момент зарегистрирована на Вашем компьютере. Перерегистрировав новый редактор, вместо старого, Ваша программа будет использовать новый редактор с другими свойствами.

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

Условное имя может использоваться различными средствами pzl-технологии для идентификации компонент как на экране, так и в сообщениях об ошибках.

Константа componentRunable_C

Эта константа сообщает о возможности запуска данной компоненты путем вызова предиката spbRun:(string UserInfo). Предикат spbRun декларируетс в одноименном интерфейсе spbRun.

Соответственно, компонента должна поддерживать интерфейс spbRun, если возможность вызова предиката spbRun предусматривается.

Если интерфейс spbRun компонентой поддерживается, то константа componentRunable_C должна иметь значение core::b_true и core::b_false - если не поддерживается.

Предикат spbRun - это, по крайней мере, один предикат в pzl-компоненте, который может этой компонентой поддерживаться и имя этого предиката известно.

Предикат spbRun имеет единственный входной параметр UserInfo типа string, назначение которого определяется создателем компоненты.

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

Это несколько напоминает функцию раздела GOAL - точка начала работы программы, но в случае с предикатом spbRun этот предикат может быть вызван, а может быть и не вызван.

Если приложение основано на использовании графического пользовательского интерфейса (pfc/GUI или pfc/VPI), то возможность запуска компоненты предикатом spbRun может означать, что будет создана какая-то форма на экране и эта форма далее позволит сделать что-то значимое (не обязательно полезное).

Если пользовательский интерфейс не используется, то предикат spbRun может быть использован и для других целей по усмотрению программиста.

Предикат spbRun:(string UserInfo) объявлен в интерфейсе spbRun, где он является единственным. Если компонента поддерживает интерфейс spbRun, то это декларируется стандартным способом:

interface componentInterface
  supports pzlComponent
  supports spbRun
 
constants
...
  ComponentRunable_C = b_true.
...
end interface componentInterface

либо один из интерфейсов, поддерживаемых базовым интерфейсом компоненты, должен поддерживать интерфейс spbRun, как показано ниже

interface componentInterface
  supports pzlComponent
  supports textEditor
 
constants
...
  componentRunable_C = b_true.
...
end interface componentInterface
 
interface textEditor
  supports spbRun
...
end interface textEditor

Константа componentMetaInfo_C

Константа componentMetaInfo_C имеет тип core::namedValue* и представляет список именованных данных, относящихся к данной компоненте - метаинформация о компоненте.

pzl-Система никак эту метаинформацию не обрабатывает, но при соотвествующем запросе к контейнеру данные этого списка доступны программисту.

Если метаинформация о компоненте отсутствует, то константа componentMetaInfo_C все равно должна быть определена и должна представлять пустой список.

constants
  componentMetaInfo_C = [].

Константа Описания Компоненты сomponentDescriptor_C

Константа сomponentDescriptor_C включает описания важнейших свойств компоненты перечисленные выше:

  • ComponentID_C,
  • ComponentAlias_C,
  • ComponentRunable_C
  • ComponentMetainfo_C

Константа сomponentDescriptor_C соответствует домену pzlDomains::pzlComponentInfo_D, объявленному в интерфейсном файле pzlDomains:

pzlComponentInfo_D=pzlComponentInfo
  (
  string Alias,
  entityUID_D ComponentID,
  booleanInt Runable,
  core::namedValue_List UserDefinedInfo
  ).

Пример описания констант pzl-компоненты

В реальной компоненте константы описания могут выглядеть так:

constants
  componentDescriptor_C:pzlDomains::pzlComponentInfo_D=pzlDomains::pzlComponentInfo
    (
    componentAlias_C,
    componentID_C,
    componentRunAble_C,
    componentMetaInfo_C
    ).
  componentID_C:pzlDomains::entityUID_D=pzlDomains::str("ComponentExample").
  componentAlias_C="ПримерКомпоненты".
  componentRunAble_C=b_True.
  componentMetaInfo_C:namedValue_List=[].

или так

constants
  componentDescriptor_C:pzlDomains::pzlComponentInfo_D=pzlDomains::pzlComponentInfo
    (
    componentAlias_C,
    componentID_C,
    componentRunAble_C,
    componentMetaInfo_C
    ).
  componentID_C:pzlDomains::entityUID_D=pzlDomains::uid(0x5701809E,0x0453,0x43A2,0xB1,0x82,0x8F,0xAE,0x2A,0x6B,0xA5,0x63).
  componentAlias_C="PzlAboutDialog".
  componentRunAble_C=b_False.
  componentMetaInfo_C:namedValue_List=
    [
    namedValue("CodeExporter",string("AboutExporter"))
    ].

Конструктор компоненты new:(object UsingObject)

Базовый класс pzl-Компоненты должен включать только один конструктор, имеющий формат new:(object UsingObject).

Здесь параметр UsingObject (что предполагает трактовку "С использованием объекта") не имеет никакого назначения и может быть использован автором pzl-Компоненты по своему усмотрению.

Например, в случае компоненты на базе GUI этот параметр может передавать объект типа window, соответствующий родительскому окну. Но если вызов конструктора в форме new(ParentWindow) автоматически преобразует тип window в тип object, то следует иметь в виду, что клауза конструктора, получит этот параметр именно как параметр типа object и непосредственное обращение к этому объекту невозможно.

Для обращения к объекту, переданному в виде параметра, необходимо привести параметр к типу, соответствующему возможному типу существующего объекта.

Например, в рассматриваемом случае с передачей объекта родительского окна для получения его высоты (height) надо привести объект ParentWindowAsObject в тип window:

clauses
  new(ParentWindowAsObject):-
    ParentWindow=convert(window,ParentWindowAsObject),
    WindowHeight=ParentWindow:getHeight(),
    ...

Особенности использования pzl-компонент как VIP-классов

Pzl-Компоненты, являясь обычными классами системы программирования Visual Prolog (VIP-классы), не требуют каких-либо специальных приемов программирования. Более того, при практическом программировании никакой разницы между использованием обычных VIP-классов и pzl-компонент нет. Тем не менее мы отметим здесь некоторые детали техники их созданиия и использования.

Создание pzl-компоненты

Создание pzl-компоненты возможно двумя способами:

Процедура создания pzl-компоненты стандартными средствами IDE не является рекомендуемой и приводится для разрешения коллизий в экстраординарных случаях

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

перечисленных требований

Компиляция проекта, содержащего созданную таким образом pzl-компоненту, может привести к генерации множества ошибок, если не "зарегистрирована" в проекте-контейнере.

Для регистрации pzl-компоненты в pzl-контейнере необходимо:

  • В файле PzlConfig.i определить константу usePzl[ИмяКомпоненты]_C:boolean=true.
  • В файле PzlConfig.pack в раздел % privately used packages добавить полное имя заголовочного файла пакета
    #include @"ИмяФайлаКомпоненты.ph"
  • В файле PzlConfig.pro:
    • в список, возвращаемый предикатом getComponentIDInfo() добавить элемент вида
      [ИмяБазовогоИнтерфейсаКомпоненты]::componentDescriptor_C
    • добавить клаузу
new([ИмяБазовогоИнтерфейсаКомпоненты]::componentID_C,Container)=Object:-
  !,
  Object = [ИмяБазовогоКлассаКомпоненты]::new(Container)

.

Обращения к pzl-Компоненте

Существуют три способа вызова создания экземпляра pzl-компоненты:

  • путем вызова конструктора new(...)
  • путем вызова предиката newByName(...)
  • путем вызова предиката newByID(...)

Первый способ выглядит следующим образом:

... 
MyClassInstance =myClass::new(SomeObject), 
MyClassInstance:callNeededPredicate() 
...

Это соответствует обычным правилам VIP.

Второй способ использует pzl-систему явно и выглядит так:

...
MyClassObj=pzl::newByName(MyClass,...), 
MyClassInstance=tryConvert(iMyClass, MyClassObj), 
MyClassInstance:сallNeededPredicate(),

Здесь iMyClass является интерфейсом класса myClass, а “MyClass” является строковым именем класса (alias) myClass.

Третий способ также явно использует pzl-систему и выглядит так:

 
...
MyClassObj=pzl::newByID(str(MyClass),...), 
MyClassInstance=tryConvert(iMyClass, MyClassObj), 
MyClassInstance:сallNeededPredicate(),

Здесь iMyClass является интерфейсом класса myClass, а str(“MyClass”) является строковым представлением идентификатора класса.

Обращение к другим классам и компонентам

Обращение к другим классам и компонентам из текущей компоненты ничем не отличается от обычного стиля обращений к классам, объектам классов и объектам компонент. Следует иметь в виду, что для обращения к компоненте в стиле

...
ОбъектКомпоненты=классКомпоненты::new(SomeObject),
ОбъектКомпоненты:предикатКомпонеты(Аргумент1,...),
...

компонента классКомпоненты должна быть представлена в проекте, из которого производится это обращение, своим классом-представителем.

Однако, для обращения к компоненте в стиле

...
ОбъектКомпонентыObj=pzl::newByName("УсловноеИмяКомпоненты",Object),
ОбъектКомпоненты=convert(интерфейсКомпоненты,ОбъектКомпонентыObj),
ОбъектКомпоненты:предикатКомпонеты(Аргумент1,...),
...

или

...
ОбъектКомпонентыObj=pzl::newByID(ИдентификаторКомпоненты,Object),
ОбъектКомпоненты=convert(интерфейсКомпоненты,ОбъектКомпонентыObj),
ОбъектКомпоненты:предикатКомпонеты(Аргумент1,...),
...

класс-представитель компоненты не требуется.

Завершение использования

По завершении использования объекта компоненты все указатели на нее должны быть удалены. Такое стандартное для VIP обращение с объектами в случае pzl-Компоненты также приводит к гибели ее объекта.

Отладка

Если pzl-компонента находится в главном (исполняемом) приложении, то отладка текста ее праграммы ничем не отличается от приемов отладки обычного класса системы Visual Prolog.

Если pzl-компонента находится в pzl-Контейнере, представляющем собой DLL, то процесс отладки должен начинаться с проекта этого контейнера (DLL). Впрочем, это типичный подход к отладке DLL, принятый в системе Visual Prolog.

Регистрация

Если какой-либо класс и pzl-компонента находятся в одной и той же сущности (.EXE или та же самая DLL), и при этом обращение производится в форме

... 
MyClassInstance =myClass::new(SomeObject), 
MyClassInstance:callNeededPredicate() 
...

то регистрация pzl-Компоненты, к которой производится обращение, может не производиться.

Во всех остальных случаях компонента должна быть зарегистрирована

  • либо в локальном файле пользователя
  • либо в реестре LocalUser системы Windows (имя раздела HKEY_CURRENT_USER)
  • либо в реестре LocalMashine системы Windows (имя раздела HKEY_LOCAL_MACHINE)

Примечание. Для регистрации компонент следует использовать специальные средства pzl-Технологии. Регистрация компонент вручную не рекомендуется.

При регистрации компоненты в реестре системы Windows используются следующие имена директорий windows-реестра (указаны в файле VpPuZzle/System/Packs/pzlDomains.i)

constants
    registryVPPuZzleAtGlobal_C = "vp_PuZzle".
    registryComponentsAtGlobal_C = "vp_PuZzle\\pzlComponents".
 
    registryVPPuZzleAtLocalUser_C = "Software\\vp_PuZzle".
    registryComponentsAtLocalUser_C = "Software\\vp_PuZzle\\pzlComponents".
    registryRunAble_C = "RunAble".

При этом для именования свойств компонент используются константы

    registryRunAbleTrue_C="True".
    registryRunAbleFalse_C="False".

Получение информации о компоненте

Как уже упоминалось, следующие предикаты объявлены в интерфейсе pzlComponent и поддерживаются содержательно pzl-системой

  • getContainerVersion() - возвращает информацию о версии контейнера, в котором данная компонента содержится
  • getContainerName() - возвращает информацию об имени файла-контейнера, в котором содержится данная компонента
  • getClassInfo(className,classVersion) - возвращает информации об имени и версии класса pzlComponent

Для того, чтобы внешние по отношению к данной компоненте средства могли бы получить информацию об идентификаторе, условном наименовании и метаинформации, рекомендуется помещать следующие клаузы в базовый класс pzl-компоненты:

caluses
  getComponentID()= componentID_C.
  getComponentAlias() = componentAlias_C.
  getComponentMetaInfo() = componentMetaInfo_C

Напоминаем, что объявления этих предикатов содержатся в интерфейсе pzlComponent.

Ограничения в несанкционированном использовании

Pzl-Компонента не содержит никаких средств для ограничения ее использования. Если программа получает доступ к контейнеру, в котором расположена pzl-Компонента, и программе известны интерфейсы для общения с компонентной, то эта pzl-Компонента оказывается доступной программе.

Вы, как автор pzl-Компоненты, можете предусмотреть дополнительные меры по ограничению прав использования pzl-Компоненты.

Например, Вы можете предусмотреть сеанс идентификации прав, в ходе которого в pzl-Компоненту должна быть передана информация, подтверждающая права на использование данной компоненты.

Если текст программы pzl-Компоненты недоступен широкому кругу пользователей, то такого рода защита от несанкционированного использования может оказаться достаточно эффективной.

Ссылки