Расширение действий
Техника расширения действий позволяет объявить абстрактное действие в одном модуле и добавлять к нему реализации в других модулях. Это отложенное построение соответствующего оператора ветвления или оператора последовательности: базовый модуль задает вид будущего оператора и требования к его реализациям, а остальные модули постепенно добавляют отдельные реализации.
Абстрактное действие задает контракт расширения: классы параметров определяют допустимые реализации, а при необходимости действие может также объявлять класс результата и классы его параметров.
Расширение действий позволяет:
- Реализовывать концепцию полиморфизма действий по аналогии с некоторыми объектно-ориентированными языками программирования.
- Убирать зависимость между модулями, добавляя определенные "точки входа" для позднего добавления нового поведения.
Выбор реализации
Какая именно реализация будет выполнена, зависит от формы абстрактного действия:
- В явной условной форме каждая реализация имеет собственное условие выбора.
- В сигнатурной полиморфной форме реализация выбирается по совместимости классов текущих аргументов с ее сигнатурой.
- В последовательной форме одна реализация не выбирается: выполняются все добавленные реализации.
Если одновременно подходят несколько реализаций в форме, где должна быть выбрана одна реализация, итоговое поведение определяется режимом взаимоисключения и порядком реализаций.
Порядок реализаций
Абстрактное действие хранит упорядоченный список реализаций. Новые реализации можно добавлять в начало или в конец этого списка.
Это влияет на поведение следующим образом:
- Если абстрактное действие допускает несколько одновременно подходящих реализаций, будет выполнена первая подходящая реализация из этого списка.
- В последовательной форме будут выполнены все реализации в порядке этого списка.
- Во взаимоисключающем режиме для каждого набора аргументов должна существовать ровно одна подходящая реализация.
Поэтому порядок реализаций является частью контракта расширения, а не просто технической деталью.
Полнота набора реализаций
Абстрактное действие может требовать, чтобы вся допустимая область значений параметров была покрыта реализациями. В этом случае для всех потомков классов параметров должна существовать хотя бы одна подходящая реализация, а во взаимоисключающем режиме - ровно одна.
Такое требование делает контракт расширения сильнее: при добавлении новых более частных случаев необходимо одновременно добавлять и соответствующие реализации.
Контракт реализации
Каждая реализация абстрактного действия имеет собственную сигнатуру параметров. Платформа соотносит эту сигнатуру с контрактом абстрактного действия.
Реализация не должна неявно сужать область применимости абстрактного действия. Иначе сигнатура реализации перестает соответствовать контракту абстрактного действия.
Если абстрактное действие объявляет результат, то возвращаемое значение и его параметры также должны соответствовать этому контракту.
Асинхронная пометка реализации
В формах, где из нескольких реализаций выбирается одна, отдельная реализация может иметь дополнительную пометку, что при построении асинхронного выполнения ее следует рассматривать как оптимистично-асинхронную.
Если у абстрактного действия есть хотя бы одна реализация с такой пометкой, именно такие реализации используются как основа для вычисления его асинхронного поведения.
Полиморфная форма
Так же как и для оператора ветвления, для абстрактного действия существует полиморфная форма, когда можно не задавать условие явно, а использовать в качестве условия принадлежность сигнатуре соответствующего этому условию действия.
Взаимоисключаемость условий
Как и для оператора ветвления, для абстрактного действия можно указать, что все его условия должны быть взаимоисключающими. В этом режиме для каждого набора аргументов должна существовать не более чем одна подходящая реализация.
Язык
В этой технике используются две языковые конструкции: оператор ABSTRACT для объявления абстрактного действия и инструкция ACTION+ для добавления к нему реализации.
Примеры
exportXls 'Выгрузить в Excel' ABSTRACT CASE OVERRIDE LAST (Order);
exportXls (Order o) + WHEN name(currency(o)) == 'USD' THEN {
MESSAGE 'Export USD not implemented';
}
CLASS ABSTRACT Task;
run 'Выполнить' ABSTRACT MULTI EXCLUSIVE FULL (Task);
CLASS Task1 : Task;
name = DATA STRING[100] (Task);
run (Task1 t) + {
MESSAGE 'Run Task1 ' + name(t);
}
CLASS ABSTRACT Animal;
whoAmI ABSTRACT ( Animal);
CLASS Dog : Animal;
whoAmI (Dog d) + { MESSAGE 'I am a dog!'; }
CLASS Cat : Animal;
whoAmI (Cat c) + { MESSAGE 'I am a cat!'; }
ask () {
FOR Animal a IS Animal DO
whoAmI(a);
}
CLASS Sku;
name = DATA STRING[100] (Sku);
onStarted ABSTRACT LIST ();
onStarted () + {
name(Sku s) <- '1';
}
onStarted () + {
name(Sku s) <- '2';
}
CLASS Human;
name = DATA STRING[100] (Human);
testName ABSTRACT CASE ( Human);
testName (Human h) + WHEN name(h) == 'John' THEN { MESSAGE 'I am John'; }
testName (Human h) + WHEN name(h) == 'Bob' THEN { MESSAGE 'I am Bob'; }
CLASS Issue;
CLASS Language;
localizedTitle = DATA STRING[100] (Issue, Language);
getLocalizedTitle(Issue issue) ABSTRACT STRING[100] (Language);
getLocalizedTitle (Issue issue) + {
FOR Language l IS Language DO
RETURN localizedTitle(issue, l);
}