(модератор 31.01.2012): загружено в коллекцию какomcBus

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

Интерфейс шины межмодульной передачи сообщений можно определить, например, так:
## Код:
```
DEFINITION Bus;

  TYPE
    Message = ABSTRACT RECORD END;

    HandleBusMessage = PROCEDURE (VAR msg: Message);

  PROCEDURE Send (IN destination: ARRAY OF CHAR; VAR msg: Message; OUT ok: BOOLEAN);

END Bus.
```

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

PROCEDURE Send (IN destination: ARRAY OF CHAR; VAR msg: Message; OUT ok: BOOLEAN);
Отправка сообщения модулю.

Предусловие:
destination - имя модуля получателя;
msg - сообщение для этого модуля;

Постусловие:
ok = TRUE если модуль destination был найден и была найдена и вызвана экспортированная им процедура HandleBusMessage. В противном случае ok = FALSE (либо такой модуль не найден, либо он не экспортирует нужной процедуры).

Пример использования транспортной шины межмодульной передачи сообщений:
## Код:
```
MODULE TestBus;

  IMPORT Log, Bus;

  TYPE
    HelloMsg = RECORD (Bus.Message) END;
  
  PROCEDURE HandleBusMessage* (VAR msg: Bus.Message);
  BEGIN
    WITH msg: HelloMsg DO
      Log.String("HelloMsg received!!!"); Log.Ln
    ELSE
    END
  END HandleBusMessage;

  PROCEDURE Test*; (* (!)TestBus.Test *)
    VAR msg: HelloMsg; ok: BOOLEAN;
  BEGIN
    Bus.Send("TestBus", msg, ok) (* Отправка сообщения msg модулю "TestBus", т.е. самому себе *)
  END Test;

END TestBus.
```

А вот и полная реализация модуля Bus средствами Meta-программирования:
## Код:
```
MODULE Bus;

  IMPORT Meta;

  TYPE
    Message* = ABSTRACT RECORD END;
    
    HandleBusMessage* = PROCEDURE (VAR msg: Message);

  PROCEDURE Send* (IN destination: ARRAY OF CHAR; VAR msg: Message; OUT ok: BOOLEAN);
    VAR m, p: Meta.Item;
    r: RECORD (Meta.Value) h: HandleBusMessage END;
  BEGIN ok := FALSE;
    Meta.Lookup(destination$, m);
    IF m.obj = Meta.modObj THEN
      m.Lookup("HandleBusMessage", p);
      IF p.obj = Meta.procObj THEN
        p.GetVal(r, ok);
        IF ok THEN r.h(msg) END
      END
    END
  END Send;

END Bus.
```

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

P. S. Следующим логическим шагом конечно же будет реализация remoting-шины, позволяющей посылать сообщения модулям запущенным на других компьютерах, т.е. по адресу: "адресIP:порт:модуль".