Перейти к основному содержимому
Версия: 7.0

Расширение свойств

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

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

Расширение свойств позволяет:

  • Реализовывать концепцию полиморфизма свойств по аналогии с некоторыми объектно-ориентированными языками программирования.
  • Убирать зависимость между модулями, добавляя определенные "точки входа" для позднего изменения способа вычисления свойств.

Выбор реализации

Какая именно реализация считается подходящей, зависит от вида абстрактного свойства:

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

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

Порядок реализаций

Абстрактное свойство хранит реализации в упорядоченном списке. Новые реализации можно добавлять в начало или в конец этого списка.

Это влияет на поведение следующим образом:

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

Поэтому порядок реализаций является частью контракта расширения, а не просто технической деталью.

Полнота набора реализаций

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

Такое требование делает контракт расширения сильнее: при добавлении новых более частных случаев необходимо одновременно добавлять и соответствующие реализации.

Контракт реализации

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

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

Полиморфная форма

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

Взаимоисключаемость условий

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

Язык

В этой технике используются две языковые конструкции: оператор ABSTRACT для объявления абстрактного свойства и инструкция += для добавления к нему реализации.

Примеры

CLASS Invoice;
CLASS InvoiceDetail;
CLASS Range;

rateChargeExchange(invoice) = ABSTRACT NUMERIC[14,6] (Invoice);
backgroundSku 'Цвет' (d) = ABSTRACT CASE FULL COLOR (InvoiceDetail);
overVAT = ABSTRACT VALUE OVERRIDE FIRST Range (InvoiceDetail);
CLASS ABSTRACT AClass;
CLASS BClass : AClass;
CLASS CClass : AClass;
CLASS DClass : AClass;

name(AClass a) = ABSTRACT BPSTRING[50] (AClass);
innerName(BClass b) = DATA BPSTRING[50] (BClass);
innerName(CClass c) = DATA BPSTRING[50] (CClass);
innerName(DClass d) = DATA BPSTRING[50] (DClass);

name(BClass b) = 'B' + innerName(b);
name(CClass c) = 'C' + innerName(c);

name[AClass](BClass b) += name(b);
name(CClass c) += name(c);
name(DClass d) += 'DClass' + innerName(d) IF d IS DClass;