В ряде объектно ориентированных языков программирования существует модификатор - "protected", отсутствующий в Оберонах. Protected используется для ограничения (для защиты) способов модификации состояния объекта разрешая protected-модификацию только в классах потомках. На первый взгляд может показаться, что раз в Оберонах нет ключевого слова protected, то в них невозможно образовать защищенные действия. В самом деле, рассмотрим классическую схему (паттерн) импорта модулей:
Код:
ПубличныйМодуль--->---РеализационныйМодуль
| |
|/ |/
| |
+---->---+ +----<----+
| |
|/ |/
| |
КлиентскийМодуль
В публичном модуле описан абстрактный тип; в реализационном модуле этот тип расширен до конкретного типа; клиентский модуль импортирует публичный модуль с интерфейсом - абстрактным типом, а также какой-то реализационный модуль (или даже несколько реализационных модулей). Для определенности, пусть публичный модуль экспортирует абстрактный тип объекта-контейнера имеющего целочисленное поле доступное только для чтения - количество элементов в контейнере:
Код:
TYPE
Контейнер* = POINTER TO ABSTRACT RECORD
количествоЭлементов-: INTEGER
END;
Поскольку поле "количествоЭлементов" доступно только для чтения, то не существует ни какого способа изменить его из других модулей. Необходимо экспортировать еще и процедуру модификатор:
Код:
PROCEDURE УстановитьНовоеКоличествоЭлементов* (контейнер: Контейнер; n: INTEGER);
BEGIN ASSERT(n >= 0, 20);
контейнер.количествоЭлементов := n
END УстановитьНовоеКоличествоЭлементов;
Иначе как же еще модифицировать это поле? Но экспортирование такой процедуры в некотором смысле опасно - ведь ее может вызвать кто угодно, когда угодно и для какого угодно объекта-контейнера тем самым, быть может, нарушив целостность этого объекта-контейнера. Вызов этой процедуры не контроллируем, т.е. не защищен. Как уже было сказано в начале этой заметки, в ряде языков программирования эта проблема решается путем введения специального модификатора "protected" и процедура становится защищенным методом. В Оберонах нет модификатора protected, поэтому указанная выше схема не работает.
Решение задачи образования защищенных действий над объектами предоставляется самой модульностью. Схема импорта модулей должна быть такой:
Код:
ЗащищенныйМодуль
| |
|/ |/
| |
+----<---+ +---->----+
| |
|/ |/
| |
ПубличныйМодуль--->---РеализационныйМодуль
| |
|/ |/
| |
+---->---+ +----<----+
| |
|/ |/
| |
КлиентскийМодуль
Защищенный модуль экспортирует защищенные действия над объектами:
Код:
MODULE ПримерыЗащищенныйМодуль;
TYPE
Контейнер* = POINTER TO ABSTRACT RECORD
количествоЭлементов-: INTEGER
END;
PROCEDURE УстановитьНовоеКоличествоЭлементов* (контейнер: Контейнер; n: INTEGER);
BEGIN ASSERT(n >= 0, 20);
контейнер.количествоЭлементов := n
END УстановитьНовоеКоличествоЭлементов;
END ПримерыЗащищенныйМодуль.
Публичный модуль, естественно, экспортирует только публичные методы:
Код:
MODULE ПримерыПубличныйМодуль;
IMPORT
Защищенный := ПримерыЗащищенныйМодуль;
TYPE
Контейнер* = POINTER TO ABSTRACT RECORD (Защищенный.Контейнер)
(*...*)
END;
PROCEDURE (контейнер: Контейнер) Положить* (элемент: ANYPTR), NEW, ABSTRACT;
PROCEDURE (контейнер: Контейнер) Изъять* (OUT элемент: ANYPTR), NEW, ABSTRACT;
END ПримерыПубличныйМодуль.
Каждый реализационный модуль импортирует защищенный и публичный модули. С помощью импортированного защищенного модуля в реализационном модуле можно выполнять над объектами защищенные действия:
Код:
MODULE ПримерыРеализационныйМодуль;
IMPORT
Защищенный := ПримерыЗащищенныйМодуль,
Публичный := ПримерыПубличныйМодуль;
TYPE
Контейнер* = POINTER TO RECORD (Публичный.Контейнер)
(* ... *)
END;
PROCEDURE (контейнер: Контейнер) Положить* (элемент: ANYPTR);
BEGIN
(*...*)
Защищенный.УстановитьНовоеКоличествоЭлементов(контейнер, контейнер.количествоЭлементов + 1);
(*...*)
END Положить;
PROCEDURE (контейнер: Контейнер) Изъять* (OUT элемент: ANYPTR);
BEGIN
(*...*)
Защищенный.УстановитьНовоеКоличествоЭлементов(контейнер, контейнер.количествоЭлементов - 1);
(*...*)
END Изъять;
PROCEDURE Новый* (VAR контейнер: Публичный.Контейнер);
VAR к: Контейнер;
BEGIN
NEW(к);
(*...*)
контейнер := к
END Новый;
END ПримерыРеализационныйМодуль.
Все клиентские модули импортируют только публичный модуль и реализационные модули но не импортируют защищенный модуль. Поскольку клиентские модули не импортируют защищенный модуль, то они и не могут выполнять над объектами защищенные действия - эти действия, стало быть, защищены от клиетов. Задача решена.
Код:
MODULE ПримерыКлиентскийМодуль;
IMPORT
Интерфейс := ПримерыПубличныйМодуль,
Реализация := ПримерыРеализационныйМодуль,
Журнал := StdLog;
PROCEDURE Выполнить*;
VAR контейнер: Интерфейс.Контейнер;
BEGIN
Реализация.Новый(контейнер);
контейнер.Положить(NIL);
Журнал.Int(контейнер.количествоЭлементов);
Журнал.Ln;
(*...*)
END Выполнить;
END ПримерыКлиентскийМодуль.
В модульных системах защита осуществляется не "волшебным" словом "protected", а просто с помощью вынесения защищенных действий в отдельный модуль с последующем контролем импорта такого защищенного модуля.