Абстрактная запись - это базовый тип, который имеет только
интерфейс, но не имеет реализации. Что это нам дает? Давайте на примере. Создаем модуль для работы с файлами в нашем каркасе, как это сделано в ББ:
Код:
MODULE Files;
(*Объявим асбтрактные типы, с которыми будут работать пользователи*)
TYPE
File = POINTER TO ABSTRACT RECORD ... END; (* Файл *)
Locator = POINTER TO ABSTRACT RECORD ... END; (* Описатель пути, НЕ сама директория - всего лишь удобная замена строковому представлению *)
Reader = POINTER TO ABSTRACT RECORD ... END;
Writer = POINTER TO ABSTRACT RECORD ... END;
(* Курьеры для доступа к файлу *)
(* Объявим методы для наших типов *)
PROCEDURE (f: File) NewReader (old: Reader), NEW, ABSTRACT;
...
А где реализация? Реализации нет и в этом модуле и не будет. Клиентские модули будут работать, естественно, с переменными каких-то конкретных типов-потомков, но даже не будут подозревать об этом. Более того, в зависимости от ситуации можно им подсовывать экземляры разных типов. Но как клиентскому модулю получить экземпляр конкретного типа, ничего не зная о нем? Для этого используется известный в ООП механизм
фабрик типа, которые в ББ называются
директории.
Объявляется опять же асбтрактный тип, через интерфейс которого клиенты могут создавать экземпляры других типов модуля:
Код:
(* Продолжаем наш Files *)
TYPE Directory = POINTER TO ABSTRACT RECORD ... END
PROCEDURE (d: Directory) New (loc: Locator; ask: BOOLEAN): File, NEW, ABSTRACT;
...
И финальный штрих - объявим экспортированную переменную-указатель на фабрику:
Код:
VAR dir-: Directory;
Все, интерфейс для клиентов закончен, больше клиентам ничего знать не надо. Они могут создавать файлы через обращение Files.dir.New(...).
Теперь плавно переходим к реализации. Финальный шрих нашего модуля Files:
Код:
VAR stdDirectory: Directory;
PROCEDURE SetDirectory (d: Directory);
BEGIN
dir := d;
IF stdDirectory = NIL THEN
stdDirectory := d
END
END SetDirectory;
Все. Каркас для файлов готов.
Теперь пишем модуль реализации:
Код:
MODULE HostFiles;
IMPORT Files; (* Обратите внимание - системщики часто это не сразу понимают - в компонентных системах низкоуровневые модули импортируют высокоуровневые, а не наоборот *)
(* Здесь мы объявляем конкретные типы - потомки абстрактных и реализовываем их *)
TYPE ...
(* Чаще всего эти типы вообще НЕ ЭКСПОРТИРУЮТСЯ. В случае с HostFiles и HostWindows это все же сделано, чтобы дать выход на некоторые конкретные нюансы платформы. Но программист должен отдавать себе отчет в том, что использование таких нюансов надо ограничивать несколькими местами своей программы, закрывая их за новыми своими типами, которые в случае чего можно быстро переписать *)
TYPE Directory = POINTER TO RECORD (Files.Directory) ... END;
Наш модуль будет прилинкован сразу после Kernel и должен будет о себе "заявить", подставив при своей загрузке свою фабрику HostFiles.Directory в Files:
Код:
PROCEDURE Init;
VAR d: Directory;
BEGIN
NEW(d);
FIles.SetDirectory(d)
END Init;
BEGIN
Init
END.
Что мы имеем? HostFiles с момента своей загрузки в память обеспечивает всю реальную работу с файлами, при этом никто из пользовательских модулей о его существовании знать не обязан и даже не должен.
Дальше начинается самое интересное. Обычной файловой системы нам мало. В ББ есть возможность припаковывать файлы к EXE, создавая своего рода виртуальную файловую систему внутри приложения. Как нам обеспечить прозрачную работу с ней? Да проще пареной репы
Делается еще один модуль HostPackedFiles, в котором реализуются File/Locator/Directory для работы с упакованными файлами.
В нем объявляется следующие процедуры:
Код:
PROCEDURE SetFilesDir;
BEGIN
(* здесь мы запоминаем текущую Files.dir, а потом подставляем новую *)
Files.SetDirectory(d)
END SetFilesDir;
PROCEDURE RestoreFilesDir;
BEGIN
Files.SetDirectory(запомненная старая директория)
END SetFilesDir;
Откуда-нибудь, например, из конфигурационного файла Config, вызываем HostPackedFiles.SetFilesDir. В Files инсталлируется новая директория.
Это директория сама решает, относится ли запрошенный файл к упакованной файловой системе или к реальной. В первом случае клиенту возвращается HostPackedFiles.File, во втором - HostFiles.File, c которыми клиент работает совершенно одинаково и даже не подозревает о хитрых манипуляциях. Потом можно отключить HostPackedFiles, если будет желание.
А захотим сделать прозрачный доступ к FTP через Files - да сколько угодно.
У меня, например, есть модуль, позволяющий работать с шифрованными файлами. Щелкаешь на коммандер - и все сохраняемые файлы начинают шифроваться, а при загрузке - расшифровываться. Щелкаешь на другой - работа с шифрованием отключается.