Pzl-Компонента: различия между версиями

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

 
(не показано 67 промежуточных версий этого же участника)
Строка 1: Строка 1:
{{copyright}}
=Необходимые условия признания класса pzl-компонентой=
=Необходимые условия признания класса pzl-компонентой=
Любой класс, порождающий объекты (имеющий интерфейс), может быть признан компонентой, если
Любой класс, порождающий объекты (имеющий интерфейс), может быть признан компонентой, если
#Имя интерфейса и имя класса различаются;
#Имя интерфейса и имя класса различаются;
#Декларация класса содержит единственный конструктор ''new(object ИспользуемыйОбъект)'';
#Интерфейс класса поддерживает интерфейс ''pzlComponent'';
#Интерфейс класса поддерживает интерфейс ''pzlComponent'';
#Интерфейс класса включает декларации констант:
#Интерфейс класса включает декларации констант:
Строка 10: Строка 10:
##''componentMetaInfo_C'' с типом ''core::namedValue*''.
##''componentMetaInfo_C'' с типом ''core::namedValue*''.
##''сomponentDescriptor_C'' с типом ''pzlDomains::pzlComponentInfo_D'';
##''сomponentDescriptor_C'' с типом ''pzlDomains::pzlComponentInfo_D'';
#Декларация класса содержит единственный конструктор ''new(object ИспользуемыйОбъект)'';
#Имплементация класса наследует от класса ''pzlComponent'';
#Имплементация класса наследует от класса ''pzlComponent'';
#Пакетный файл деклараций класса (.PH) включает файл ''"System\Packs\pzlcomponent\pzlcomponent.ph".''
#Пакетный файл деклараций класса (.PH) включает файл ''"System\Packs\pzlcomponent\pzlcomponent.ph".''
Ниже приведены фрагменты различных составляющих компоненты, удовлетворяющей перечисленным выше условиям.
Ниже приведены фрагменты различных составляющих компоненты, удовлетворяющей перечисленным выше условиям.


'''Декларация класса''' содержит коструктор new(...) и имя интерфейса отличается от имени класса. При этом класс содержит единственный конструктор ''new:(object UsingObject)''
'''Декларация класса'''
:Содержит единственный коструктор ''new:(object UsingObject)''. Имя интерфейса отличается от имени класса.
<vip>
<vip>
class сomponentExample:iComponentExample
class сomponentExample:iComponentExample
Строка 23: Строка 25:
end class
end class
</vip>
</vip>
'''Интерфейс класса''' содержит квалификатор '''''supports pzlComponent''''' и декларацию константы '''''componentDescriptor_C''''' типа '''''pzlDomains::pzlComponentInfo_D'''''
'''Интерфейс класса'''
:Cодержит квалификатор '''''supports pzlComponent''''' и декларацию констант
:*componentID_C
:*componentAlias_C
:*componentRunAble_C=b_True
:*componentMetaInfo_C
:*componentDescriptor_C
 
<vip>
<vip>
interface iComponentExample
interface iComponentExample
Строка 47: Строка 56:
end interface iComponentExample
end interface iComponentExample
</vip>
</vip>
'''Имплементация класса''' содержать директиву наследования '''''inherits pzlComponent''''' и содержит клаузу для конструктора '''''new(UsingObject)'''''
'''Имплементация класса'''
:Содержит директиву наследования '''''inherits pzlComponent''''' и содержит клаузу для конструктора '''''new(UsingObject)'''''
<vip>
<vip>
implement componentExample
implement componentExample
Строка 54: Строка 64:
clauses
clauses
   new(UsingObject):-
   new(UsingObject):-
    pzlComponent::new(),
     ...
     ...


Строка 62: Строка 71:
end implement componentExample
end implement componentExample
</vip>
</vip>
'''Файл деклараций пакета''' ComponentExample.PH содержит директиву включения файла '''''PzlComponent.ph'''''
'''Файл деклараций пакета''' ComponentExample.PH  
:Содержит директиву включения файла '''''PzlComponent.ph'''''
<vip>
<vip>
#requires ...
#requires ...
Строка 70: Строка 80:
</vip>
</vip>


=Константа Описания Компоненты сomponentDescriptor_C=
=Интерфейс pzlComponent=
Константа описания pzl-компоненты является важной составной частью ее интерфейса. Эта константа описывает важные свойства компоненты, необходимые для ее использования. Константа сomponentDescriptor_C соответствует домену pzlDomains::pzlComponentInfo_D, объявленному в интерфейсном файле pzlDomains:
Интерфейс pzlComponent соответствует наследуемому pzl-Компонентой классу pzlComponent и делает класс pzl-компонентой, связывая ее с другими частями pzl-системы. Кроме того, этот интерфейс позволяет получать часть информации о компоненте.
<vip>
 
pzlComponentInfo_D=pzlComponentInfo
В частности следующие предикаты поддерживаются содержательно pzl-системой
  (
*getContainerVersion() - возвращает информацию о версии контейнера, в котором данная компонента содержится
  string Alias,
*getContainerName() - возвращает информацию об имени файла-контейнера, в котором содержится данная компонента
  entityUID_D ComponentID,
*getClassInfo(className,classVersion) - возвращает информации об имени и версии класса pzlComponent
  booleanInt Runable,
 
  core::namedValue_List UserDefinedInfo
Остальные предикаты интерфейса:
  ).
*getComponentVersion()-> string
</vip>
*getComponentID() -> entityUID_D
===Параметр ComponentID===
*getComponentAlias() -> string
Этот параметр описания определяет идентификатор компоненты, который (по-возможности) должен быть уникальным в глобальном смысле. То есть две программы в глобальном информационном пространстве (не говоря уже об одном копьютере), использующие компоненты с одним и тем же идентификатором могут быть уверены, что они используют точно одну и ту же pzl-компоненту. Этого можно добиться, если использовать способ, аналогичный способу, применяемому Microsoft для идентификации COM-компонент.
*getComponentMetaInfo() -> namedValue*
*release
должны быть определены пользователем в имплементации базового класса компоненты. В противном случае генерируют исключения при обращении.
 
=Предопределенные константы интерфейса pzl-компоненты=
===Константа componentID_С===
Константа сomponentID_С определяет идентификатор компоненты, который (по-возможности) должен быть уникальным в глобальном смысле. То есть две программы в глобальном информационном пространстве (не говоря уже об одном копьютере), использующие компоненты с одним и тем же идентификатором могут быть уверены, что они используют точно одну и ту же pzl-компоненту. Этого можно добиться, если использовать способ, аналогичный способу, применяемому Microsoft для идентификации COM-компонент.
Для этого в pzl-технологии используется структура с функтором uid:
Для этого в pzl-технологии используется структура с функтором uid:
<vip>
<vip>
Строка 110: Строка 126:
   uid(unsined16,...).
   uid(unsined16,...).
</vip>
</vip>
===Параметр Alias (Условное имя)===
Наряду с уникальным идентификатором, pzl-технология предоставляет воможность присвоить каждой компоненте условное, содержательное имя, являющееся строкой символов. Однако уникальность Условного Имени (Alias) не является жестким требованием.


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


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


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


=Пример описания 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, то это декларируется стандартным способом:
<vip>
interface componentInterface
  supports pzlComponent
  supports spbRun
 
constants
...
  ComponentRunable_C = b_true.
...
end interface componentInterface
</vip>
либо один из интерфейсов, поддерживаемых базовым интерфейсом компоненты, должен поддерживать интерфейс spbRun, как показано ниже
<vip>
interface componentInterface
  supports pzlComponent
  supports textEditor
 
constants
...
  componentRunable_C = b_true.
...
end interface componentInterface
 
interface textEditor
  supports spbRun
...
end interface textEditor
</vip>
 
===Константа componentMetaInfo_C===
Константа componentMetaInfo_C имеет тип core::namedValue* и представляет список именованных данных, относящихся к данной компоненте - метаинформация о компоненте.
 
pzl-Система никак эту метаинформацию не обрабатывает, но при соотвествующем запросе к контейнеру данные этого списка доступны программисту.
 
Если метаинформация о компоненте отсутствует, то константа componentMetaInfo_C все равно должна быть определена и должна представлять пустой список.
<vip>
constants
  componentMetaInfo_C = [].
</vip>
 
===Константа Описания Компоненты сomponentDescriptor_C===
Константа сomponentDescriptor_C включает описания важнейших свойств компоненты перечисленные выше:
*ComponentID_C,
*ComponentAlias_C,
*ComponentRunable_C
*ComponentMetainfo_C
Константа сomponentDescriptor_C соответствует домену pzlDomains::pzlComponentInfo_D, объявленному в интерфейсном файле pzlDomains:
<vip>
pzlComponentInfo_D=pzlComponentInfo
  (
  string Alias,
  entityUID_D ComponentID,
  booleanInt Runable,
  core::namedValue_List UserDefinedInfo
  ).
</vip>
 
===Пример описания констант pzl-компоненты===
В реальной компоненте константы описания могут выглядеть так:
<vip>
<vip>
constants
constants
Строка 137: Строка 229:
   componentMetaInfo_C:namedValue_List=[].
   componentMetaInfo_C:namedValue_List=[].
</vip>
</vip>
или так
<vip>
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"))
    ].
</vip>
=Конструктор компоненты new:(object UsingObject)=
Базовый класс pzl-Компоненты должен включать только один конструктор, имеющий формат
'''new:(object UsingObject)'''.
Здесь параметр UsingObject (что предполагает трактовку "С использованием объекта") не имеет никакого назначения и может быть использован автором pzl-Компоненты по своему усмотрению.
Например, в случае компоненты на базе GUI этот параметр может передавать объект типа window, соответствующий родительскому окну. Но если вызов конструктора в форме new(ParentWindow) автоматически преобразует тип window в тип object, то следует иметь в виду, что клауза конструктора, получит этот параметр именно как параметр типа object и непосредственное обращение к этому объекту невозможно.
Для обращения к объекту, переданному в виде параметра, необходимо привести параметр к типу, соответствующему возможному типу существующего объекта.
Например, в рассматриваемом случае с передачей объекта родительского окна для получения его высоты (height) надо привести объект ParentWindowAsObject в тип window:
<vip>
clauses
  new(ParentWindowAsObject):-
    ParentWindow=convert(window,ParentWindowAsObject),
    WindowHeight=ParentWindow:getHeight(),
    ...
</vip>
=Особенности использования pzl-компонент как VIP-классов=
Pzl-Компоненты, являясь обычными классами системы программирования Visual Prolog (VIP-классы), не требуют каких-либо специальных приемов программирования. Более того, при практическом программировании никакой разницы между использованием обычных VIP-классов и pzl-компонент нет. Тем не менее мы отметим здесь некоторые детали техники их созданиия и использования.
==Создание pzl-компоненты==
Создание pzl-компоненты возможно двумя способами:
*[[Elementary PzlStudio#Создание Pzl-Компоненты|Специальными средствами pzl-технологии]]
*Стандартными средствами IDE ('''не рекомендуется''')
Процедура создания pzl-компоненты стандартными средствами IDE  '''не является рекомендуемой''' и приводится для разрешения коллизий в экстраординарных случаях
*Создать '''в проекте, являющемся pzl-контейнером''' новый класс в пакете, который будет всегда связан с этим классом
*При создании класса указать, что класс порождает объекты
*После создания создания класса вручную модифицировать код для обеспечения всех ранее
[[#Необходимые_условия_признания_класса_pzl-компонентой|'''перечисленных требований''']]
Компиляция проекта, содержащего созданную таким образом pzl-компоненту, может привести к генерации множества ошибок, если не "зарегистрирована" в проекте-контейнере.
Для регистрации pzl-компоненты в pzl-контейнере необходимо:
*В файле PzlConfig.i определить константу '''''usePzl[ИмяКомпоненты]_C:boolean=true'''''.
*В файле PzlConfig.pack в раздел % privately used packages добавить полное имя заголовочного файла пакета<br/><vip>#include @"ИмяФайлаКомпоненты.ph"</vip>
*В файле PzlConfig.pro:
**в список, возвращаемый предикатом getComponentIDInfo() добавить элемент вида<br/> <vip>[ИмяБазовогоИнтерфейсаКомпоненты]::componentDescriptor_C</vip>
**добавить клаузу<br/>
<vip>new([ИмяБазовогоИнтерфейсаКомпоненты]::componentID_C,Container)=Object:-
  !,
  Object = [ИмяБазовогоКлассаКомпоненты]::new(Container)</vip>.
==Обращения к pzl-Компоненте==
Существуют три способа вызова создания экземпляра pzl-компоненты:
*путем вызова конструктора new(...)
*путем вызова предиката newByName(...)
*путем вызова предиката newByID(...)
Первый способ выглядит следующим образом:
<vip>
...
MyClassInstance =myClass::new(SomeObject),
MyClassInstance:callNeededPredicate(…)
...
</vip>
Это соответствует обычным правилам VIP.
Второй способ использует pzl-систему явно и выглядит так:
<vip>
...
MyClassObj=pzl::newByName(“MyClass”,...),
MyClassInstance=tryConvert(iMyClass, MyClassObj),
MyClassInstance:сallNeededPredicate(…),
</vip>
Здесь iMyClass является интерфейсом класса myClass, а “MyClass” является строковым именем класса (alias)  myClass.
Третий способ также явно использует pzl-систему и выглядит так:
<vip>
...
MyClassObj=pzl::newByID(str(“MyClass”),...),
MyClassInstance=tryConvert(iMyClass, MyClassObj),
MyClassInstance:сallNeededPredicate(…),
</vip>
Здесь iMyClass является интерфейсом класса myClass, а str(“MyClass”) является строковым представлением идентификатора класса.
==Обращение к другим классам и компонентам==
Обращение к другим классам и компонентам из текущей компоненты ничем не отличается от обычного стиля обращений к классам, объектам классов и объектам компонент.
Следует иметь в виду, что для обращения к компоненте в стиле
<vip>
...
ОбъектКомпоненты=классКомпоненты::new(SomeObject),
ОбъектКомпоненты:предикатКомпонеты(Аргумент1,...),
...
</vip>
компонента ''классКомпоненты'' должна быть представлена в проекте, из которого производится это обращение, своим классом-представителем.
Однако, для обращения к компоненте в стиле
<vip>
...
ОбъектКомпонентыObj=pzl::newByName("УсловноеИмяКомпоненты",Object),
ОбъектКомпоненты=convert(интерфейсКомпоненты,ОбъектКомпонентыObj),
ОбъектКомпоненты:предикатКомпонеты(Аргумент1,...),
...
</vip>
или
<vip>
...
ОбъектКомпонентыObj=pzl::newByID(ИдентификаторКомпоненты,Object),
ОбъектКомпоненты=convert(интерфейсКомпоненты,ОбъектКомпонентыObj),
ОбъектКомпоненты:предикатКомпонеты(Аргумент1,...),
...
</vip>
класс-представитель компоненты не требуется.
==Завершение использования==
По завершении использования объекта компоненты все указатели на нее должны быть удалены. Такое стандартное для VIP обращение с объектами в случае pzl-Компоненты также приводит к  гибели ее объекта.
==Отладка==
Если pzl-компонента находится в главном (исполняемом) приложении, то отладка текста ее праграммы ничем не отличается от приемов отладки обычного класса системы Visual Prolog.
Если pzl-компонента находится в pzl-Контейнере, представляющем собой DLL, то процесс отладки должен начинаться с проекта этого контейнера (DLL). Впрочем, это типичный подход к отладке DLL, принятый в системе Visual Prolog.
==Регистрация==
Если какой-либо класс и pzl-компонента находятся в одной и той же сущности (.EXE или та же самая DLL), и при этом обращение производится в форме
<vip>
...
MyClassInstance =myClass::new(SomeObject),
MyClassInstance:callNeededPredicate(…)
...
</vip>
то регистрация pzl-Компоненты, к которой производится обращение, может не производиться.
Во всех остальных случаях компонента должна быть зарегистрирована
*либо в локальном файле пользователя
*либо в реестре LocalUser системы Windows (имя раздела HKEY_CURRENT_USER)
*либо в реестре LocalMashine системы Windows (имя раздела HKEY_LOCAL_MACHINE)
'''Примечание.''' Для регистрации компонент следует использовать специальные средства pzl-Технологии. Регистрация компонент вручную не рекомендуется.
При регистрации компоненты в реестре системы Windows используются следующие имена директорий windows-реестра (указаны в файле VpPuZzle/System/Packs/pzlDomains.i)
<vip>
constants
    registryVPPuZzleAtGlobal_C = "vp_PuZzle".
    registryComponentsAtGlobal_C = "vp_PuZzle\\pzlComponents".
    registryVPPuZzleAtLocalUser_C = "Software\\vp_PuZzle".
    registryComponentsAtLocalUser_C = "Software\\vp_PuZzle\\pzlComponents".
    registryRunAble_C = "RunAble".
</vip>
При этом для именования свойств компонент используются константы
<vip>
    registryRunAbleTrue_C="True".
    registryRunAbleFalse_C="False".
</vip>
==Получение информации о компоненте==
Как уже упоминалось, следующие предикаты объявлены в интерфейсе pzlComponent и поддерживаются содержательно pzl-системой
*getContainerVersion() - возвращает информацию о версии контейнера, в котором данная компонента содержится
*getContainerName() - возвращает информацию об имени файла-контейнера, в котором содержится данная компонента
*getClassInfo(className,classVersion) - возвращает информации об имени и версии класса pzlComponent
Для того, чтобы внешние по отношению к данной компоненте средства могли бы получить информацию об [[#Константа_componentID_С|'''идентификаторе''']], [[#Константа_ componentAlias_C_(Условное_имя)|'''условном наименовании''']] и [[#Константа_componentMetaInfo_C|'''метаинформации''']], рекомендуется помещать следующие клаузы в базовый класс pzl-компоненты:
<vip>
caluses
  getComponentID()= componentID_C.
  getComponentAlias() = componentAlias_C.
  getComponentMetaInfo() = componentMetaInfo_C
</vip>
Напоминаем, что объявления этих предикатов содержатся в интерфейсе pzlComponent.
==Ограничения в несанкционированном использовании==
Pzl-Компонента не содержит никаких средств для ограничения ее использования. Если программа получает доступ к контейнеру, в котором расположена pzl-Компонента, и программе известны интерфейсы для общения с компонентной, то эта pzl-Компонента  оказывается доступной программе.
Вы, как автор pzl-Компоненты, можете предусмотреть дополнительные меры по ограничению прав использования pzl-Компоненты.
Например, Вы можете предусмотреть сеанс идентификации прав, в ходе которого в pzl-Компоненту должна быть передана информация, подтверждающая права на использование данной компоненты.
Если текст программы pzl-Компоненты недоступен широкому кругу пользователей, то такого рода защита от несанкционированного использования может оказаться достаточно эффективной.
=Ссылки=
[[Категория:VpPuZzle]]
[[Категория:VpPuZzle]]

Текущая версия на 15:16, 21 ноября 2007

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

Необходимые условия признания класса 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-Компоненты недоступен широкому кругу пользователей, то такого рода защита от несанкционированного использования может оказаться достаточно эффективной.

Ссылки