Расширение свойств
Техника расширения свойств позволяет объявить абстрактное свойство в одном модуле, а определять его реализации в других модулях. Это отложенное построение свойства, вычисляемого как соответствующий оператор выбора: базовый модуль задает вид будущего оператора и требования к его реализациям, а остальные модули постепенно добавляют отдельные реализации.
Абстрактное свойство задает контракт расширения: класс результата определяет возвращаемое значение, а классы параметров - допустимые реализации.
Расширение свойств позволяет:
- Реализовывать концепцию полиморфизма свойств по аналогии с некоторыми объектно-ориентированными языками программирования.
- Убирать зависимость между модулями, добавляя определенные "точки входа" для позднего изменения способа вычисления свойств.
Выбор реализации
Какая именно реализация считается подходящей, зависит от вида абстрактного свойства:
- В явной условной форме каждая реализация имеет собственное условие выбора.
- В сигнатурной полиморфной форме реализация выбирается по совместимости классов текущих аргументов с ее сигнатурой.
- В форме выбора по значению реализация считается подходящей, если она возвращает определенное значение.
Если одновременно подходят несколько реализаций, итоговый результат определяется режимом взаимного исключения и порядком реализаций.
Порядок реализаций
Абстрактное свойство хранит реализации в упорядоченном списке. Новые реализации можно добавлять в начало или в конец этого списка.
Это влияет на поведение следующим образом:
- Если абстрактное свойство допускает несколько одновременно подходящих реализаций, результат будет возвращен первой подходящей реализацией из этого списка.
- Во взаимоисключающем режиме для каждого набора аргументов должна существовать ровно одна подходящая реализация.
Поэтому порядок реализаций является частью контракта расширения, а не просто технической деталью.
Полнота набора реализаций
Абстрактное свойство может требовать, чтобы вся допустимая область значений параметров была покрыта реализациями. В этом случае для всех потомков классов параметров должна существовать хотя бы одна подходящая реализация, а во взаимоисключающем режиме - ровно одна.
Такое требование делает контракт расширения сильнее: при добавлении новых более частных случаев необходимо одновременно добавлять и соответствующие реализации.
Контракт реализации
Каждая реализация абстрактного свойства имеет собственную сигнатуру параметров и собственный класс результата. Платформа соотносит их с контрактом абстрактного свойства.
Реализация не должна сужать сигнатуру абстрактного свойства. В частности, в явной условной форме это обычно означает, что все параметры должны участвовать либо в условии выбора, либо в вычислении результата. Иначе такая реализация будет иметь более узкую сигнатуру и не сможет корректно расширить абстрактное свойство.
Полиморфная форма
Так же как и для оператора выбора, для абстрактного свойства существует полиморфная форма, когда условие выбора и соответствующий ему результат задаются одним свойством. Соответственно, как и в операторе выбора, условием может являться либо принадлежность сигнатуре этого свойства, либо само это свойство.
Взаимоисключаемость условий
Как и для оператора выбора, для абстрактного свойства можно указать, что все его условия должны быть взаимоисключающими. В этом режиме для каждого набора аргументов должна существовать не более чем одна подходящая реализация.
Язык
В этой технике используются две языковые конструкции: оператор 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;