PowerShell: “позднее” связывание или “плагины” для сценариев
Я понимаю, что для скриптового языка (коим и является powershell) позднее связывание звучит абсурдно, мягко говоря (“раннего” там и быть не может). Но иного термина на нашёл.
Суть в следующем: имеем некую задачу, в рамках которой подзадачи появляются всё новые и новые. Например – анализируем мы журнал SMTP на предмет ошибок и потенциальных проблем. А вариантов анализа появляется всё больше и больше. Чтобы при этом сохранить читабельность сценария, логично разбить его на модули. Тут и возникла идея унификации “интерфейса” модуля (плагина такого своеобразного для конкретной задачи). И идея использования этого унифицированного интерфейса некими алгоритмами, которые знают только интерфейс модуля, но не знают имён модулей, их количества, и вообще – созданы были до “плагинов”.
Сразу перейду к примеру, иначе так и не будет ясно, о чём здесь написано.
Допустим, для некоторой задачи мы разработали унифицированный интерфейс модуля-“плагина”. И разработали два плагина: test1.psm1:
function EntryPoint () { 'test1___'; };
и плагин test2.psm1:
function EntryPoint () { 'test2___'; };
Для примера специально привёл такой вот “вырожденный” интерфейс модуля – всего один “метод”, без аргументов, возвращающий строку.
Теперь разработаем ещё один модуль с унифицированными алгоритмами, ориентированными только на описанный нами интерфейс модуля – testProcessing.psm1:
function Get-Plugins () { get-module | ? {$_.ExportedCommands.Contains('EntryPoint')} | % {$_.AsCustomObject()}; };
Да, пока мы реализовали только один “алгоритм” – вызываем для всех загруженных модулей, имеющих описанный выше интерфейс (то есть – для наших плагинов), метод EntryPoint
.
Ну и “главный” файл сценария (типа main.ps1):
Import-Module '.\testProcessing' #-force Import-Module '.\test1' #-force Import-Module '.\test2' #-force $plugins = get-plugins();
$plugins | % {$_.entryPoint()};
Пример, безусловно, примитивен, но, тем не менее, демонстрирует ту самую возможность “позднего” связывания. Теперь для расширения функционала какого-либо сложного сценария (в моём случае – сценария анализа логов SMTP сервера) достаточно создать “плагин” (модуль с унифицированным интерфейсом) и подключить его через import-module
, более – ничего не требуется. Сам же сценарий ориентирован на использование коллекции модулей.
Чтобы довести идею до верха маразма, приведу скорректированную версию main.ps1:
Import-Module '.\testProcessing' #-force dir '.\plugins\*.psm1' | % {$_.fullName} | import-module #–force $plugins = get-plugins(); $plugins | % {$_.entryPoint()};
Как видно, теперь наш сценарий в принципе до запуска не знает, какие плагины он будет использовать. Он “подгрузит” все плагины, которые обнаружит в подкаталоге plugins. Удобно в какой-то мере, main.ps1 вообще править теперь не надо при добавлении / удалении плагинов!
Но не всегда “удобно” использовать модуль как объект. В некоторых случаях нам могут потребоваться фильтрующие функции, которые никак не могут быть методами объекта. Поэтому предлагаю несколько иной механизм использования “плагинов”:
# найдём все плагины $plugins = dir '.\plugins\*\*.psm1' | % {$_.fullName}; # используем плагины "по очереди" $plugins ` | import-module -force -passThru ` | % { Select-SMTPLogSessionsByPlugin -log $log ` | Get-SMTPLogPluginReport ` | % { ... }; $_; } ` | remove-module ;
Идея в следующем: загружаем модули плагинов по очереди, и выгружаем сразу после использования. В приведённом примере Get-SMTPLogPluginReport
и Select-SMTPLogSessionsByPlugin
— “командлеты” из модуля плагина. И, как видно, такой подход позволил нам использовать фильтрующую функцию из модуля плагина в конвейере powershell.
Вот такое вот несколько извращённое применение паттерна проектирования “точка интерфейса” без классов и виртуальных методов, исключительно на модулях PowerShell!
P.S. Сейчас эту идею применяю для анализа журнала SMTP сервера. Как только закончу – выложу сценарии, уже построенные по вышеописанному модульному принципу.
RSS комментарии
Обратная ссылка