А вот и последняя глава 23.4 книги "Programming in Oberon". Позволю себе представить собственный перевод, сделанный исключительно для себя - чтоб лучше "въехать" в тему

Programming in Oberon

A derivative of Programming in Modula-2 (1982)
Niklaus Wirth

23.4. Обработчики и сообщения.

В разделе 3 была подчёркнута важность правильной декомпозиции (модуляризации) систем. Модуль ограничивает видимость объявлений и включает в себя концепцию скрытия информации. В объектно-ориентированных языках определение типа, то есть объявление класса, принимает на себя как роль модуля, так и роль единицы раздельной компиляции. В Обероне мы предпочитаем отделять конструкции, выражающие скрытие информации, от конструкций для определения типов и процедур.
Таким образом, модуль может содержать одно или несколько определений типов, или же не содержать ни одного. Тем не менее, взгляд на тип данных и его операторы как на логическую единицу, скрывающую детали реализации, является старой и проверенной практикой в концепции абстрактного типа данных.
В частности, для сложных объектов требуется, чтобы различные подтипы могли быть определены и реализованы отдельными командами программистов, и могут пройти годы от определения базисной части до опубликования или дистрибуции. Нужно иметь возможность определять каждый производный тип в его собственном модуле. Это необходимо как для  базовых типов с операциями, применяемыми ко множеству объектов (в отличие от отдельных объектов), определённых в базовом модуле, так и для определений производных типов в клиентских модулях.
Среди таких операций мы выделяем так называемое широковещание (broadcast), то есть, обход структуры, содержащей объекты (список, дерево) с применением метода к каждому элементу.  Объявление таких бродкастов в основе своей ограничено базовым модулем.
Однако, подтип может вводить особые операции, в том числе и такие, которые не доступны другим подтипам. Очевидно, что такие операции должны выполниться при бродкасте, процедура которого определена в базовом модуле, даже если новые операции введены спустя годы после определения базовой функциональности. Базовые определения, однако, не могут быть ни изменены, ни открыты для доступа, потому что они уже давно распределены по клиентуре. Как же разрешить эту дилемму?
Решение заключено в замене набора методов одной-единственной процедурой, которая выделяется среди других методов. Такую процедуру мы назовём обработчиком (handler). Обработчик имеет только два параметра: объект, к которому он применяется и идентификатор операции с соответствущими параметрами. Второй параметр обработчика  имеет форму записи (record) и мы назовём его сообщение. Сообщение может обозначать любую операцию, даже вводимую в будущем. Такая возможность используется для введения новых, производных типов, и называется оно «расширение типа» (type extension). Позвольте объяснить это решение на примере графического редактора.

Во-первых, объявим базовый тип следующим образом:

## Код:
```
FigureDesc = RECORD 
  x, y, w, h: INTEGER;  (*координаты, ширина и высота объекта*) 
  handle: Handler; 
  next: Figure   (*следующий элемент в списке объектов*) 
END ;
```


Процедурный тип Обработчик объявим так:

## Код:
```
Handler = PROCEDURE (f: Figure; VAR msg: Message)
```

и новый тип Сообщение, как пустую и расширяемую запись:

## Код:
```
Message = RECORD END
```


Если какие-то операции, применяемые ко всем объектам, уже известны в момент определения базового типа, то подтипы нашего сообщения объявляются примерно так:

## Код:
```
DrawMsg  =  RECORD (Message) END ; 
MarkMsg  =  RECORD (Message) on: BOOLEAN END ; 
MoveMsg  =  RECORD (Message) dx, dy: INTEGER END ;
```


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

## Код:
```
PROCEDURE Broadcast(VAR msg: Message); 
  VAR f: Figure; 
BEGIN f := root;  (*root – глобальная переменная в базовом модуле*) 
  WHILE f # NIL DO f.handle(f, msg); f := f.next END 
END Broadcast
```

Любой новый подтип фигуры будет определён в новом клиентском модуле, импортирующем базовый модуль Figures. С определением нового подтипа будет ассоциирован обработчик, шаблон объявления которого приведён ниже. Этот обработчик инсталлируется в поле handle каждого объекта типа Rectangle.

## Код:
```
PROCEDURE Handle(f: Figure; VAR msg: Message); 
VAR r: Rectangle; 
BEGIN r := f(Rectangle); 
  IF msg IS DrawMsg THEN  (*нарисовать прямоугольник r*) 
  ELSIF msg IS MarkMsg THEN MarkRectangle(r, msg(MarkMsg).on) 
  ELSIF msg IS MoveMsg THEN 
    INC(r.x, msg(MoveMsg).x); INC(r.y, msg(MoveMsg).y) 
  ELSIF … 
  END 
END Handle
```


Операция перемещения объекта путём указания смещения dX и dY выглядит как нечто составное:

## Код:
```
VAR msg: MoveMsg; 
msg.dx := dX; msg.dy := dY; f.handle(f, msg)
```

Однако, мы должны помнить, что сообщения посылаются объектам главным образом опосредованно. Другими словами, получатель сообщения неизвестен. Вот пример широковещания:

## Код:
```
msg.dx := dX; msg.dy := dY; Broadcast(msg)
```


Особая гибкость этой техники, использующей обработчики и сообщения, иногда определяется как полиморфизм, поскольку расширяемость опирается на тот факт, что «неизвестные» сообщения попросту игнорируются. Они «проваливаются» через каскад конструкции if – elsif в обработчике объекта, которому сообщение не предназначено.
Для примера, к нашему редактору добавим новый модуль, и назовём его Blinkers. Он содержит объявление производного типа Blinker ( BlinkerDesc), в котором введён новый метод, заставляющий объект мерцать в заданном ритме. Нам понадобится производное сообщение BlinkMsg. Вызов обработчика любого другого объекта в глобальном списке объектов, через который распространяются сообщения, не приведёт ни к какому эффекту, поскольку конструкция

## Код:
```
ELSIF msg IS BlinkMsg THEN …
```


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

Подводя итоги, можно сказать следующее:

1.Объект представлен как указатель на запись. Запись содержит процедурное поле, называемое handle. Процедура, присвоенная этому полю, называется обработчиком и принимает два параметра. Первый параметр определяет объект, чей обработчик вызывается. Второй параметр определяет операцию, это параметр VAR, является записью и называется сообщением.
2.Обработчик задаёт поведение объекта и вызывается посредством сообщения, которое обычно является специфичным для данного объекта расширением типа Message.
3.Сообщение, передаваемое как параметр, указывает действие, относящееся к его типу. Поля записи сообщения составляют параметры действия. Их количество и типы специфичны для конкретного типа сообщения и действия.
4.Обработчик содержит каскад if – elsif. Проверки типов позволяют разделить подтипы сообщений и соответствующие им действия.
5.Присвоение обработчика объекту называется инсталлированием.
6.Действие осуществляется, во-первых, установкой параметра сообщения, а во-вторых, посылкой сообщения объекту.
7.Сообщения распространяются широковещательным способом всем объектам, имеющим разнородные структуры данных, причём знать природу этой структуры не нужно. Неподходящие сообщения просто игнорируются.

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

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

Процедурный стиль программирования, который является самым подходящим для многих приложений, и объектно-ориентированный стиль могут прекрасно сосуществовать в Обероне. Если кто-то желает программировать в традиционном, процедурном стиле, он может просто проигнорировать объектно-ориентированные средства, и они не будут стоять на пути.