Как создать гибкую архитектуру плагинов?


154

Повторяющейся темой в моей работе по разработке было использование или создание собственной архитектуры подключаемого модуля. Я видел, что он подходит ко многим параметрам - файлам конфигурации (XML, .conf и т. Д.), Структурам наследования, информации о базе данных, библиотекам и другим. По моему опыту:

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

Поскольку я стремлюсь учиться у различных архитектур, с которыми я работал, я также жду предложений от сообщества. Как вы реализовали архитектуру плагина SOLID? Какой был ваш худший провал (или худший провал, который вы видели)? Что бы вы сделали, если бы собирались реализовать новую архитектуру плагинов? Какой SDK или проект с открытым исходным кодом, с которым вы работали, имеет лучший пример хорошей архитектуры?

Несколько примеров, которые я нашел самостоятельно:

Эти примеры, кажется, играют на разных сильных сторонах языка. Обязательна ли хорошая архитектура плагинов к языку? Лучше ли использовать инструменты для создания архитектуры плагинов, или делать это на своих собственных следующих моделях?


Можете ли вы сказать, почему архитектура плагинов была общей темой? Какие проблемы он решает или какие цели он решает? Многие расширяемые системы / приложения используют плагины некоторой формы, но форма значительно варьируется в зависимости от решаемой проблемы.
mdma

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

Ответы:


89

Это не столько ответ, сколько куча потенциально полезных замечаний / примеров.

  • Один из эффективных способов сделать ваше приложение расширяемым - это представить его внутреннюю часть как язык сценариев и написать все вещи верхнего уровня на этом языке. Это делает его вполне изменяемым и практически пригодным для будущего (если ваши примитивы правильно выбраны и реализованы). История успеха такого рода вещей - Emacs. Я предпочитаю это системе плагинов в стиле eclipse, потому что если я хочу расширить функциональность, мне не нужно изучать API и писать / компилировать отдельный плагин. Я могу написать трехстрочный фрагмент в текущем буфере, оценить его и использовать. Очень плавная кривая обучения и очень приятные результаты.

  • Одним из приложений, которое я немного расширил, является Trac . Он имеет компонентную архитектуру, которая в этой ситуации означает, что задачи делегируются модулям, которые объявляют точки расширения. Затем вы можете реализовать другие компоненты, которые будут вписываться в эти точки и изменить поток. Это немного похоже на предложение Калки выше.

  • Еще один хороший - это py.test . Он следует философии «лучший API - это не API» и основан исключительно на вызовах, вызываемых на каждом уровне. Вы можете переопределить эти ловушки в файлах / функциях, названных в соответствии с соглашением, и изменить поведение. Вы можете увидеть список плагинов на сайте, чтобы увидеть, насколько быстро / легко они могут быть реализованы.

Несколько общих замечаний.

  • Постарайтесь, чтобы ваше нерасширяемое / не изменяемое пользователем ядро ​​было как можно меньше. Передайте все, что можете, более высокому уровню, чтобы увеличить расширяемость. Меньше материала для исправления в ядре, чем в случае неудачного выбора.
  • С этим связано то, что вы не должны принимать слишком много решений относительно направления вашего проекта с самого начала. Реализуйте наименьшее необходимое подмножество и затем начните писать плагины.
  • Если вы встраиваете язык сценариев, убедитесь, что он полный, на котором вы можете писать обычные программы, а не игрушечный язык только для вашего приложения.
  • Уменьшите шаблон как можно больше. Не беспокойтесь о создании подклассов, сложных API, регистрации плагинов и тому подобном. Постарайтесь сделать это простым, чтобы его было легко, а не просто возможно расширить. Это позволит использовать ваш API плагинов и побудит конечных пользователей писать плагины. Не только разработчики плагинов. py.test делает это хорошо. Затмения, насколько я знаю, нет .

1
Я хотел бы расширить точку зрения языка сценариев на то, что стоит рассмотреть встраивание существующего языка, такого как python или perl
Руди

Настоящий Руди. Многие языки делаются так, чтобы они были встроены. Guile например, сделан только для встраивания. Это экономит время, если вы делаете свое приложение сценарием. Вы можете просто зайти на один из этих языков.
Нуфал Ибрагим

24

По своему опыту я обнаружил, что на самом деле существует два типа подключаемых архитектур.

  1. Каждый следует за тем, Eclipse modelчто предназначено для обеспечения свободы и является открытым.
  2. Другой обычно требует, чтобы плагины следовали за, narrow APIпотому что плагин будет выполнять определенную функцию.

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

Различие тонкое, и иногда нет различий ... вы хотите оба для вашего приложения.

У меня нет большого опыта работы с Eclipse / открытием вашего приложения для модели плагинов (статья в посте Калки великолепна). Я читал немного о том, как затмение делает вещи, но не более того.

Блог о свойствах Yegge немного говорит о том, как использование шаблона свойств обеспечивает плагины и расширяемость.

Большая часть работы, которую я проделал, использовала архитектуру плагинов, чтобы позволить моему приложению получать доступ к плагинам, таким как время / отображение / данные карты и т. Д.

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

  • Теперь я обычно DI frameworkделаю большую часть этой работы.

Мне все еще приходится писать адаптеры для использования сторонних библиотек, но они обычно не так уж и плохи.


@ Просто да, DI = внедрение зависимости.
вывод

14

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

http://www.eclipse.org/articles/Article-Plug-in-architecture/plugin_architecture.html


Конечно, хороший пример, но он больше и сложнее, чем требуется для многих приложений?
justkt

Да, это может быть, увеличение гибкости сопровождается издержками. Но я думаю, что это показывает другую точку зрения. Почему меню вашего приложения не может быть плагином или основным видом?
Патрик

6
Тот же подход (все это плагин) используется в новой версии платформы Symfony. Их называют связками, но принцип тот же. Его архитектура довольно проста, возможно, вам стоит взглянуть на нее: symfony-reloaded.org/quick-tour-part-4
Marijn Huizendveld

@Marjin Huizendveld - вы должны добавить это в качестве отдельного ответа, чтобы я мог подтвердить ответ. Выглядит как интересный каркас!
justkt

11

Я опишу довольно простую технику, которую я использовал в прошлом. Этот подход использует отражение C #, чтобы помочь в процессе загрузки плагина. Эту технику можно изменить, чтобы она применима к C ++, но вы теряете удобство использования отражения.

IPluginИнтерфейс используется для определения классов, реализующих плагинов. В интерфейс добавлены методы, позволяющие приложению взаимодействовать с плагином. Например, Initметод, который приложение будет использовать для инструктирования плагина для инициализации.

Для поиска плагинов приложение сканирует папку плагинов для сборок .Net. Каждая сборка загружена. Отражение используется для сканирования классов, которые реализуют IPlugin. Экземпляр каждого класса плагина создан.

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

InitМетод вызывается для каждого объекта плагин. IApplicationПередается ссылка на объект, который реализует интерфейс приложения: (или что-то еще, названное специально для вашего приложения, например, ITextEditorApplication).

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

Эта система плагинов может быть расширена до языков сценариев, например Lua, путем создания производного класса плагинов, например, LuaPluginкоторый перенаправляет IPluginфункции и интерфейс приложения в сценарий Lua.

Этот метод позволяет итеративно реализовать свои IPlugin, IApplicationи другие приложения конкретных интерфейсов в процессе разработки. Когда приложение завершено и подвергнуто рефакторингу, вы можете документировать свои открытые интерфейсы, и у вас должна быть хорошая система, для которой пользователи могут создавать свои собственные плагины.


7

Обычно я использую MEF. Managed Extensibility Framework (или MEF для краткости) упрощает создание расширяемых приложений. MEF предлагает возможности обнаружения и компоновки, которые вы можете использовать для загрузки расширений приложений.

Если вы заинтересованы, читайте больше ...


Спасибо, выглядит интересно. Насколько легко применять аналогичные принципы на других языках?
Юстект

На самом деле MEF только для .NET Framework Я ничего не знаю о других платформах
BALKANGraph

7

Однажды я работал над проектом, который должен был быть настолько гибким, чтобы каждый клиент мог настроить систему, и единственный хороший дизайн, который мы нашли, - это отправить клиенту компилятор C #!

Если спецификация заполнена такими словами, как:

  • гибкий
  • Вставной
  • Настраиваемый

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

Поддержка клиентов (или людей, занимающихся поддержкой fount-line) при написании плагинов намного сложнее, чем архитектура


5

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

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

Чтобы это работало, система сценариев должна иметь достаточно надежный API с привязками к данным приложения, логике и графическому интерфейсу, а также базовые функции импорта и выполнения кода из библиотек. Кроме того, сценарии обычно должны быть безопасными в том смысле, что приложение может корректно восстанавливаться из плохо написанного сценария. Использование системы сценариев в качестве слоя косвенности означает, что приложение может легче отсоединиться в случае Something Bad ™.

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


3

Я думаю, что вам нужно сначала ответить на вопрос: «Какие компоненты должны быть плагинами?» Вы хотите, чтобы это число было абсолютным минимумом или количество комбинаций, которые вы должны проверить, взрывается. Попробуйте отделить ваш основной продукт (который не должен иметь слишком большую гибкость) от функциональности плагина.

Я обнаружил, что принцип IOC (Inversion of Control) (читай springframework) хорошо работает для обеспечения гибкой базы, к которой можно добавить специализацию, чтобы упростить разработку плагинов.

  • Вы можете отсканировать контейнер на предмет «интерфейс как объявление типа плагина».
  • Вы можете использовать контейнер для внедрения общих зависимостей, которые могут потребоваться плагинам (например, ResourceLoaderAware или MessageSourceAware).

1

Шаблон плагина - это программный шаблон для расширения поведения класса с помощью чистого интерфейса. Часто поведение классов расширяется наследованием классов, когда производный класс перезаписывает некоторые виртуальные методы класса. Проблема этого решения заключается в том, что оно конфликтует с сокрытием реализации. Это также приводит к ситуациям, когда производный класс становится местом сбора несвязанных расширений поведения. Кроме того, сценарии используются для реализации этого шаблона, как упомянуто выше: «Сделайте внутреннее устройство скриптовым языком и напишите все вещи верхнего уровня на этом языке. Это делает его вполне изменяемым и практически пригодным для будущего». Библиотеки используют библиотеки управления сценариями. Язык сценариев позволяет осуществлять высокоуровневые манипуляции с библиотеками более низкого уровня. (Также как упомянуто выше)

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.