Хотел ответить каждому, но накопилось много ответов, так что буду отвечать всем
1. "Message Bus" не панацея, есть множество способов как можно не потерять производительность (например, введя поле id).
2. Интерфейсов, как правило, много не реализуют в одном объекте (мне максимум встетилось 1 раз 7 интерфейсов). Проверка 1 или 7 последовательных значений не сильно усложнит проверку типа, и не сильно её затормозит.
3. Препроцессоры в обероне смысла не имеют! Поясняю:
Одними из ключевых принципов Оберона (как концепции, а не как языка) является единство исходного кода и реализации плюс одноразовость компиляции (нет необходимости в перекомпиляции исходников при каждой сборке). Что мы получаем в случае предпроцессора: Сгенерёный символьный файл и бинарный модуль будут отличатся от исходного файла, плюс (внимание, любители ifdef) возможно получение различного бинарного кода (символьного файла) при перекомпиляции (если будут другие условия). Вирт стремился к максимальной переносимости кода между системами и максимальной единообразности кода в различных системах, а препроцессоры способствуют получению разного кода в одной и той же системе (при чуть изменившихся условиях).
Уж лучше тогда подумать, как реализовать в обероне дженерики. Если оно вообще нужно.
С интерфейсами - всё проще. Интерфейс - это контракт, такой же как и запись, только на имена процедур у конкретного объекта. т.е., что конкретный объект будет обладать данными процедурами.
Например:
Figure* = OBJECT
VAR
x, y : REAL;
color : LONGINT;
PROCEDURE &New*(x, y : REAL; color : LONGINT);
BEGIN
SELF.x := x;
SELF.y := y;
SELF.color := color;
END New;
END Figure;
Drawable* = INTERFACE
PROCEDURE Draw*(canvas : Raster.Raster);
END Drawable;
Point* = OBJECT(Figure, Drawable)
PROCEDURE Draw*(canvas : Raster.Raster);
BEGIN
canvas.PutPixel(x, y, color);
END Draw;
END Point;
GeomList* = ARRAY OF Drawable;
Т.е. принципиально, всегда в любой системе, у модуля будет один и тот же код, только реализуемая логика может получится значительно проще, чем без интерфейсов. Для интерфейсных методов, можно предусмотреть реакции по умолчанию. Т.е. если процедура не реализована в объекте, то она либо ничего не возвращает (пустая процедура), либо возвращает 0/0.0/NIL.