Содержание

Метод расширения абстракций System

Предпосылки

Системный слой BlackBox представлен набором абстрактных интерфейсов, отвечающих за те или иные функции, доступные компонентам. Эти абстракции представляют собой минимально необходимые возможности для полноценной работы. В текущей версии интерфейсы подобраны так, что являются платформо-независимыми. А при дальнейшем расширении абстракций необходимо учитывать, что платформо-зависимые интерфейсы могут привести к «протеканию» абстракции.

На примере простой абстракции местоположение файла Files.Locator (далее локатор) рассмотрим возможные варианты решения.

Русская документация по BlackBox

Файловый локатор представляет папку в файловой системе.
Локаторы используются в Блэкбоксе и иногда в командах, которые работают с файлами, не принадлежащими Блэкбоксу.

Таким образом, локатор представлен объектом в памяти. В большинстве случаев реализация локатора содержит указание на физическую папку на диске компьютера или в сети, и путь к такой папке представлен текстовой строкой в определенном формате, специфичном для ОС.

Однако расширяемый интерфейс локатора позволяет реализовать местоположение файла, которое не поддерживает выражение в виде строки. Следовательно, абстрактный интерфейс получения полного текстового пути к файлу является слишком конкретным и ограничивает применение локатора.

Поэтому в Files.Locator подобная возможность отсутствует.

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

Что требуется?

Расширить возможности объекта базового типа Files.Locator в ряде задач при сохранении неизменности интерфейса Files.Locator, сохранение возможности работы с альтернативными реализациями локатора, а так же минимизация платформо-зависимости прикладного компонента.

Варианты решения

Изменение алгоритма компонента

Наиболее очевидный способ: изменение алгоритма компонента. Действительно, в ряде случаев возможно изменение алгоритмов работы таким образом, что доступ к строке станет не нужен, а базового интерфейса локатора будет достаточно.

Прямое использование реализации локатора

Наиболее простой способ, так как мы точно знаем, какие возможности может дать конкретная реализация локатора. Полная платформо-зависимость компонента становится платой за простоту.

Герметизация абстракции локатора внутри компонента

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

Возможность выделения конкретной реализации хост-компонента в общеупотребимую подсистему/компонент ведет к еще одной проблеме - разрастание интерфейса базового класса хост-компонента. Так как существует бесчисленное количество ситуаций применения, возможны ситуации, когда существующих возможностей станет недостаточно, и разработчики будут вносить все новые методы и функции, что вызовет несовместимость различных версий реализации и проблему хрупкости базового класса.

Использование возможностей расширения абстракции

Учитывая проблему возможного раздувания интерфейса, а так же тот факт, что единственным способом расширения интерфейса локатора является наследование, предлагается выполнить отвязку доступных функций локатора от интерфейса базового класса. Используем возможности шаблона «Объект-сообщение» для обеспечения параметрического полиморфизма методов локатора (в рамках языка Компонентный Паскаль полиморфизм реализуется данным образом). Для этого опишем класс-наследник локатора: OberFiles.Locator, в интерфейс которого добавим метод .HandleMsg(VAR msg: ANYREC). Реализацию этого локатора разместим в модуле OberHostFiles.Locator. В данной реализации мы используем возможности модуля HostFiles, для простоты. При этом, понятно, что реализация локатора с хэндлером может быть размещена сразу в реализации файловой подсистемы BlackBox, так как OberFiles.Locator по-прежнему является абстрактным типом. Важной частью системы является механизм автоматического приведения типа Files.Locator к типу HostFiles.Locator, при этом фактический результат операции приведения может содержать объект-обертку, так как с различными реализациями локатора могут быть связаны различные модули-реализации OberFiles.Locator (например, HostFiles ← OberHostFiles).

PROCEDURE GuardLoc*(loc: Files.Locator): Locator;
	VAR ret: Locator;
BEGIN
	ASSERT(loc#NIL, 20);
	WITH loc: Locator DO
		ret:=loc;
	ELSE
		IF hook#NIL THEN ret:=hook.GuardLoc(loc) END;
	END;
	RETURN ret
END GuardLoc;

Опираясь на базовый тип ANYREC мы описываем протокол (набор типов сообщений) взаимодействия с локатором, при этом, сам локатор не зависит от этих протоколов, они обрабатываются только в реализации OberHostFiles. Таким образом, описав сообщение GetPathStringMsg мы получаем возможность запросить у локатора текстовый путь. Также подобные протоколы могут быть описаны в сторонних компонентах. Такие протоколы могут иметь разную детализацию аспектов платформы, тем самым позволяя разработчику гибко регулировать платформо-зависимость своего прикладного компонента.

VAR loc: Files.Locator; ober: OberFiles.Locator; gpm: OberProtocol.GetPathStringMsg;
BEGIN
	loc:=Files.dir.This('');
	ober:=OberFiles.GuardLoc(loc);
	IF ober#NIL THEN
		ober.HandleMsg(gpm);
	ELSE Log.String('данный тип фс ещё не поддерживается'); Log.Ln; END;
	IF gpm.path#NIL THEN Log.String(gpm.path$); Log.Ln END;
END;

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

Результаты

Реализовав описанный способ расширения мы решили проблему наращивания функциональности локатора без изменения базового типа, избежав проблему появления хрупкого базового класса с помощью параметрического полиморфизма, а платформо-зависимость клиентских модулей может регулироваться посредством выбора нужного протокола. Таким образом, подсистема с набором подобных уточняющих абстракций, размещенная в общеупотребимой (а возможно и стандартной для сборки BlackBox) подсистеме сможет обеспечить развитие возможностей с сохранением обратной совместимости компонентов и интерфейсов.

Пример использования

Прототип реализации для эталона BlackBox размещен в подсистеме Sith.

Кушнир П. М. 2013/12/20 21:34