Классы. Статические и динамические сущности

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

Классы

Каждый класс должен иметь декларацию класса и имплементацию.

Декларация класса содержит набор объявлений констант, доменов, предикатов, видимых извне.

Имплементация класса содержит

  • собственно реализацию предикатов (клаузы), объявленных в декларации,
  • декларации констант,
  • декларации и реализации предикатов, используемых в клаузах,
  • декларации фактов.
  • Факты и предикаты, объявленные в имплементации класса, извне не видны.

Cтатический класс - класс не способный порождать объекты (экземпляры или точные копии). Все предикаты и разделы фактов статического класса существуют в единственном экземпляре во всем проекте.

Динамический класс - класс, способный порождать объекты (экземпляры или точные копии), Динамические классы могут содержать как статические, так и динамические предикаты, а также статические и динамические факты. Каждый экземпляр динамического класса содержит свою копию динамического предиката и свою копию динамического раздела фактов. Создание объекта (экземпляра или копии класса) осуществляется вызовом предиката-конструктора. Как правило, это предикат-функция new(), но может быть и любой другой, объявленный как конструктор в разделе constructors декларации класса.

Статический класс - это класс, не имеющий интерфейса. Или, что то же самое, - если класс не имеет инерфейса, то он - статический,

Если класс имеет интерфейс - то он - динамический.

Статические классы

Например, класс staticClass является статическим классом.

class staticClass
end class staticClass
 
implement staticClass
end implement staticClass

но этот класс ничего не способен делать. Чтобы что-то делать, добавим в этот класс декларации предикатов и клаузы.

class staticClass
predicates
   visibleStaticPred:().
end class staticClass
 
implement staticClass
clauses
     visibleStaticPred():-
       ...
       invisibleStaticPred(),
       ...         
 
class predicates
   invisibleStaticPred:().
clauses
   invisibleStaticPred():-
       ...
end implement staticClass

Обращения к предикатам этого класса записываются как

...
staticClass::staticVisiblePred(),
...

(существенно использования именно :: для обращения к статической сущности)

Здесь предикат invisibleStaticPred является предикатом, объявленным внутри имплементации, и только внутри этой имплементации может быть вызван. То есть предикаты, объявленные в декларации класса, являются заведомо всегда статическими предикатами, а раздел объявлений предикатов внутри имплементации статического класса всегда должен имет вид class predicates Если попробовать создать экземпляр класса staticClass с помощью вызова конструктора new()

...
staticClass::new(),
...

то это не пропустит компилятор. Таким образом, статические классы - это просто модули проекта. Проект, построенный только на модулях (статических классах) - не имеет никакого отношения к объекто-ориентированной концепции программирования.

Динамические классы

Например, класс dynamicClass является динамическим классом, что определяется конструкцией dynamicClass:justInterface.

class dynamicClass:justInterface
end class dynamicClass 
 
implement dynamicClass 
end implement dynamicClass 
 
interface justInterface
end interface justInterface

Опять-таки, синтаксически все правильно, но практически бесполезно. Теперь добавим какие-нибудь полезности и вынесем вперед декларацию интерфейса.

interface justInterface
predicates
   visibleDynamicPred:().
end interface justInterface
 
class dynamicClass:justInterface
predicates
   visibleStaticPred:().
end class dynamicClass 
 
implement dynamicClass 
clauses
   visibleStaticPred():-
      ...
      invisibleStaticPred(),
      ...         
 
clauses
   visibleDynamicPred():-
     ...
     invisibleStaticPred(),
     ...         
 
class predicates
   invisibleStaticPred:().
clauses
   invisibleStaticPred():-
       ...
 
predicates
   invisibleDynamicPred:().
clauses
   invisibleDynamicPred():-
       ...
 
end implement dynamicClass

Здесь мы имеем четыре предиката, которые имеют различные динамические особенности. visibleDynamicPred - динамический предикат, принадлежащий интерфесу justInterface, видимый извне. visibleStaticPred - статический предикат, видимый извне. invisibleStaticPred - статический предикат, определенный в имплементации класса dynamicClass, невидимый извне. invisibleDynamicPred - динамический предикат, определенный в имплементации класса dynamicClass, невидимый извне. К такому классу можно обратиться как к статическому

...
dynamicClass::visibleStaticPred(),
...

но нельзя так:

...
dynamicClass::visibleDynamicPred(),
...

Чтобы вызвать предикат visibleDynamicPred(), необходимо с помощью конструктора создать экземпляр класса и обратиться к этому экземпляру:

...
ЭкземплярDynamicClass=dynamicClass::new(),
ЭкземплярDynamicClass:visibleDynamicPred(),
...

Примечания:

  • Конструктор new() является предопределенным конструктором, объявление которого не требуется, если в имплементации нет для него клауза;
  • В обращении к экземпляру класса (объекту) используется разделитель ':' (а не '::', как этого требует обращение к статической сущности).

Факты

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

implement example
...
class facts
  myFact_F:(string,string).
...
end implement example

В имплементациях динамических классов факты могут быть объявлены либо как статические, либо как динамические:

class example1:example1interface
predicates
  запомнитьГород:(string Город).
  запомнитьИмяАдрес:(string Имя, string Адрес).
  получитьГород:()->string Город.
  получитьИмяАдрес:(string Имя, string Адрес).
end class example1
 
implement example1
...
class facts
  город_V:string Город.
...
facts
  имяАдрес_F:(string Адрес, string Имя).
 
clauses
  запомнитьГород(Город):-
    город_V:=Город.
...
clauses
  запомнитьИмяАдрес(Имя,Адрес):-
    assert(имяАдрес_F:(Адрес,Имя)).
 
clauses
  получитьГород(город_V).
  ...
clauses
  получитьИмяАдрес(Имя,Адрес):-
    имяАдрес_F(Адрес,Имя),
    !,
    ...
end implement example1

В приведенном примере класса example1 факт город_V является статическим, а факты имяАдрес_F(...) являются динамическими, что определяется разницей в имени раздела class facts и просто facts. Поскольку статическая сущность любого класса всего одна, а динамическая - по одной на каждый экземпляр - то и в нашем примере запрос получитьГород(), переданный любому экземпляру, вернет один и тот же результат, а запрос получитьИмяАдрес(...) вернет для каждого экземпляра свое значение.

Особенности использования фактов-переменных

В примере выше использовался факт-переменная город_V, особенностью которого является то, что такой факт всегда один и он сам несет значение терма, объявленного в декларации.

class facts
  город_V:string Город.

Здесь факт город_V имеет строковое значение. Возможны значения любых объявленных доменов, например

domains
  мойДомен=персона(string Имя,unsigned Возраст).
class facts
  персона_V:мойДомен.

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

domains
  мойДомен=персона(string Имя,unsigned Возраст).
class facts
  персона_V:мойДомен:=персона("Старик",80).
  город_V:string:="НеИзвестен".

В динамических классах начальное значение вместо декларации можно задать в клаузе конструктора

implement dynamicClass 
domains
  мойДомен=персона(string Имя,unsigned Возраст).
class facts
  персона_V:мойДомен.
clauses
  new():-
     персона_V:=персона("Старик",80),
     ...
end implement dynamicClass

Бывает, что конкретное начальное значение установить невозможно по смыслу реализации. Тогда начальное значение может быть установлено как "неустановленное".

class facts
  город_V:string:=erroneous.

где erroneous - ключевое слово языка. Факт со значением erroneous не может быть использован (точнее: при попытке его использования будет выдана соответствующая ошибка), можно только проверить является ли значение факта неустановленным с помощью детерминированного предиката isErroneous(...)

implement dynamicClass 
domains
  мойДомен=персона(string Имя,unsigned Возраст).
class facts
  город_V:string:=erroneous.
  персона_V:мойДомен.
clauses
  new():-
     персона_V:=персона("Старик",80),
     ...
clauses
  ...
     isErroneous(город_V),
     ...
end implement dynamicClass

С учетом сказанного наш пример

class facts
  город_V:string Город.

на этапе компиляции выдаст ошибку, поскольку статический факт не инициализирован при объявлении.

Константы и домены

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

<имя декларации класса или интерфейса>::<имя домена или константы>.

Взаимоотношени статических и динамических сущностей

Клаузы предикатов, объявленных статическими, не могут обращаться к динамическим сущностям (предикатам и фактам). То есть нельзя так:

implement dynamicClass 
clauses
  staticPred():-
    ...
    dynamicPred(),
    ...
end implement dynamicClass

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