Улучшение проектирования программного обеспечения C# с использованием архитектуры плагинов

Улучшение проектирования программного обеспечения C# с использованием архитектуры плагинов

19 марта 2024 г.

Моя одержимость созданием приложений на основе архитектуры плагинов, вероятно, проистекает из моего опыта игры в ролевые игры. Для этих игр обычно требовались системы, механика которых могла быть расширена с помощью плагинов или новый контент, который можно было добавить в игру с помощью плагинов. Поскольку большая часть моего раннего опыта изучения C# в детстве заключалась в создании игр, архитектура плагинов в приложениях C#< /a> легко стал тем, о чем я регулярно узнавал.

В этой статье я познакомлю вас с концепцией архитектуры плагинов и конкретно рассмотрю архитектуру плагинов в C#, а также то, как мы можем исследовать загрузку информации о плагинах. Мы также рассмотрим некоторые примеры ситуаций, в которых плагины могут быть полезны, но их более подробная реализация — это упражнение, которое вы можете выполнить в качестве домашнего задания! Давайте углубимся в это!


Понимание архитектуры плагинов

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

Использование архитектуры плагинов в приложениях C# дает ряд преимуществ, некоторые из них:

  1. Модульность: благодаря архитектуре плагинов различные компоненты приложения можно разрабатывать отдельно как плагины. Эта модульность позволяет вам сосредоточиться на конкретных функциях, не влияя на общую структуру приложения. Это также упрощает обслуживание и обновление, поскольку плагины можно добавлять или заменять независимо.
  2. 2. Гибкость. Используя архитектуру плагинов, вы можете легко добавлять в приложение новые функции или возможности без изменения основного кода. Такая гибкость обеспечивает быструю разработку и итерацию, поскольку новые плагины можно создавать и интегрировать с меньшими усилиями, чем изменение основного общего кода.

    3. Возможность повторного использования кода. Плагины могут разрабатываться как повторно используемые компоненты, которые можно использовать в нескольких проектах или приложениях. Возможность повторного использования не только экономит время разработки, но также способствует согласованности кода и снижает вероятность появления ошибок.

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

    В целом, архитектура плагинов на C# — это то, что я активно использую в своей разработке, чтобы обеспечить модульный и гибкий подход к созданию программного обеспечения.

    Посмотрите это видео, чтобы узнать, как я использую системы плагинов в своем приложении на основе вертикальных срезов:

    https://youtu.be/5OKLiQM2y30?embedable=true


    Загрузка плагинов в C

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

    Давайте рассмотрим некоторые из этих подходов и методов на примерах кода. В следующих примерах предположим, что у нас есть интерфейс IPlugin, который просто сопоставляется со следующим кодом:

    public IPlugin
    {
        void Execute();
    }
    

    Динамическая загрузка сборки с отражением в CSharp

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

    Вот пример того, как этого можно добиться:

    // Get the path to the plugin DLL
    string pluginPath = "path/to/plugin.dll";
    
    // Load the plugin assembly
    Assembly assembly = Assembly.LoadFrom(pluginPath);
    
    // Instantiate the plugin types
    IEnumerable<Type> pluginTypes = assembly
        .GetTypes()
        .Where(t => typeof(IPlugin).IsAssignableFrom(t) && !t.IsAbstract);
    
    // Create instances of the plugins
    List<IPlugin> plugins = new();
    foreach (Type pluginType in pluginTypes)
    {
        IPlugin plugin = (IPlugin)Activator.CreateInstance(pluginType);
        plugins.Add(plugin);
    }
    
    // Use the plugins
    foreach (IPlugin plugin in plugins)
    {
        plugin.Execute();
    }
    

    В приведенном выше коде мы сначала загружаем сборку плагина, используя Assembly.LoadFrom. Затем мы используем отражение, чтобы найти все типы, реализующие интерфейс IPlugin и не являющиеся абстрактными. Мы создаем экземпляры этих типов плагинов с использованием отраженияв частности, с помощью Activator.CreateInstance — и сохраните их в списке. Наконец, мы можем использовать плагины, вызвав их метод Execute.

    Autofac для динамической загрузки плагинов C#

    Одна из моих любимых средств внедрения зависимостей в C# — Autofac . Мы можем использовать Autofac для сканирования каталога на наличие сборок, содержащих типы, реализующие интерфейс IPlugin, и зарегистрировать их. Вот пример настройки Autofac на C# для этой цели:

    using Autofac;
    using System.Reflection;
    
    public class PluginLoader
    {
        public IContainer LoadPlugins(string pluginsPath)
        {
            var builder = new ContainerBuilder();
    
            // Scan the plugins directory for assemblies
            // and register types that implement IPlugin
            var types = Directory
                .GetFiles(pluginsPath, "*.dll")
                .Select(Assembly.LoadFrom)
                .ToArray();
            builder
                .RegisterAssemblyTypes(types)
                .AssignableTo<IPlugin>()
                .As<IPlugin>();
    
            return builder.Build();
        }
    }
    

    Приведенный выше код попросит использовать отражение, чтобы получить типы из сборок в каталоге плагинов. В этом случае он будет пытаться загрузить любой файл с расширением DLL из этой папки. Оттуда мы можем попросить Autofac зарегистрировать эти типы, но мы используем метод AssignableTo<T>() для фильтрации типов только для IPlugin. Наконец, мы указываем, что их можно разрешить как экземпляры IPlugin с помощью метода As<T>().

    Следующий пример кода создает контейнер, регистрирует все плагины, а затем имитирует поведение предыдущего примера кода, где мы вызываем Execute() для каждого плагина:

    // Assuming the plugins are located in a 'plugins' folder in the application directory
    var pluginFolder = Path.Combine(
        Directory.GetCurrentDirectory(),
        "plugins");
    var pluginLoader = new PluginLoader();
    var container = pluginLoader.LoadPlugins(pluginFolder);
    
    // Resolve all implementations of IPlugin and execute them
    foreach (var plugin in container.Resolve<IEnumerable<IPlugin>>())
    {
        plugin.Execute();
    }
    

    Я также сделал видеоуроки о том, как использовать Autofac для загрузки плагинов. Посмотрите это ниже:

    https://youtu.be/-pxwL_VD4Uo?embedable=true


    Примеры практических архитектур плагинов на C

    Теперь, когда мы увидели, как можно реализовать загрузку плагинов, пришло время рассмотреть некоторые примеры возможных архитектур плагинов в приложениях C#. В этих примерах мы не будем создавать полноценное приложение, но мы можем подробно остановиться на некоторых общих деталях варианта использования, о том, как может выглядеть пример API плагина и почему плагины могут быть полезны.

    Плагины динамической визуализации данных

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

    Вот пример кода, который мы могли бы рассмотреть:

    // Interface for the visualization plugin
    public interface IVisualizationPlugin : IDisposable
    {
        void Initialize();
    
        void RenderData(
            DataSet data,
            IVisualizationContext context);
    }
    
    // Example plugin implementation
    public class BarChartPlugin : IVisualizationPlugin
    {
        public void Initialize()
        {
            // Initialize the bar chart visualization
        }
    
        public void RenderData(
            DataSet data,
            IVisualizationContext context)
        {
            // Render the bar chart with the provided data
        }
    
        public void Dispose()
        {
            // Cleanup resources used by the bar chart
        }
    }
    

    В этом примере нам может потребоваться несколько дополнительных точек входа для плагинов для настройки. Это будет наш метод Initialize(). Мы помечаем каждый из них как IDisposable, чтобы гарантировать, что у каждого плагина будет надлежащая возможность очистить ресурсы, когда мы будем готовы их выгрузить.

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

    Плагины на основе расширений для обработки файлов

    Другим примером возможной архитектуры плагинов в C# является расширение возможностей обработки файлов. Представьте, что у вас есть приложение для обработки файлов, которое поддерживает различные форматы файлов, такие как изображения, документы и видео. Внедрив систему плагинов, вы можете позволить пользователям динамически разрабатывать и загружать обработчики пользовательских форматов файлов.

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

    // Interface for the file format plugin
    public interface IFileFormatPlugin
    {
        bool CanHandleFile(string filePath);
    
        void ProcessFile(string filePath);
    }
    
    // Example plugin implementation
    public class PDFPlugin : IFileFormatPlugin
    {
        public bool CanHandleFile(string filePath)
        {
            return filePath.EndsWith("pdf", StringComparison.OrdinalIgnoreCase);
        }
    
        public void ProcessFile(string filePath)
        {
            // Perform PDF-specific file processing
        }
    }
    

    Плагины механизма пользовательских правил

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

    В приведенном ниже примере кода мы используем шаблон TryX, который обычно имеет логический тип возвращаемого значения и выходной параметр для получения результата. В этом случае мы можем вывести экземпляр исключения, если оценка правила не выполняется. Альтернативным способом сделать это может быть использование пользовательского мультитипа для получения собственного типа возвращаемого значения, который может быть результатом или ошибкой, или использование пакета OneOf NuGet. Давайте проверим:

    // Interface for the rule plugin
    public interface IRulePlugin
    {
        string RuleName { get; }
    
        bool TryEvaluateRule(
            object target,
            out Exception error);
    }
    
    // Example plugin implementation
    public class AgeValidationPlugin : IRulePlugin
    {
        public string RuleName { get; } = "AgeValidation";
    
        public bool TryEvaluateRule(
            object target,
            out Exception error)
        {
            // Check if the target object meets the age validation criteria
        }
    }
    

    Системы аутентификации на основе плагинов

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

    Вот сильно упрощенный API плагина, который демонстрирует цель, но любой, кто работал с реальными системами аутентификации, вероятно, понимает, что это очень упрощенно:

    // Interface for the authentication plugin
    public interface IAuthenticationPlugin
    {
        bool AuthenticateUser(string username, string password);
    }
    
    // Example plugin implementation
    public class OAuthPlugin : IAuthenticationPlugin
    {
        public bool AuthenticateUser(string username, string password)
        {
            // Authenticate the user using OAuth protocol
        }
    }
    

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

    Платформа показателей социальных сетей

    Очень актуальным примером архитектуры плагина на C# является отслеживание показателей социальных сетей. В своем информационном бюллетене я поделился закулисными подробностями того, как это создавалось, и в итоге создал серию видеороликов, которые вы можете посмотреть на YouTube. В нем подробно описан весь пошаговый процесс создания приложения. Вы можете увидеть начало серии сборок Blazor в этом видео, в котором основное внимание уделяется архитектуре плагинов на C#:

    https://youtu.be/qndnxPzjrow?embedable=true


    Завершение архитектуры плагинов на C

    Использование архитектуры плагинов при разработке на C# — один из моих любимых способов проектирования систем. Признаюсь, у меня к ним сильное предубеждение! Для меня, когда я создаю приложения самостоятельно, они соответствуют тому, как мне нравится думать о возможности расширения программного обеспечения. Поскольку я привык структурировать код для динамической загрузки через системы плагинов, это во многом определяет мой дизайн. Однако, как и во всем, есть свои плюсы и минусы и часто нет смысла усложнять тривиальные приложения плагинами — особенно если есть функции, расширять которые не планируется.

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

    :::информация Также опубликовано здесь.

    :::


    Оригинал
PREVIOUS ARTICLE
NEXT ARTICLE