Системный слой 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