Добавление поддержки плагинов:Интерфейс или базовый класс для наследования?

StackOverflow https://stackoverflow.com/questions/543116

Вопрос

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

Зачем мне выбирать для наследования интерфейс вместо базового класса плагина?Подскажите, какой дизайн мне выбрать и почему?

Это было полезно?

Решение

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

Что касается выбора между интерфейсом и абстрактным классом, если вы выберете MAF или MEF, некоторые из них будут сделаны за вас.Самые основные различия в этом контексте заключаются в том, что, предоставляя абстрактный базовый класс, вы предоставляете точку наследования для пользовательских плагинов (написанных вами или другими разработчиками) и гарантируете, что определенные методы/свойства обеспечивают поведение по умолчанию, и заставляете производные классы реализовывать определенные необходимые методы/свойства.Недостаток заключается в том, что вы ограничены одним базовым классом.

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

Другие советы

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

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

Почему должно быть то или другое?

Вы можете определить контракт, которому должны следовать плагины, как интерфейс, на который везде ссылаются.

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

public interface IPlugin
{
    void Initialize ();
    void Stuff ();
}

public class PluginBase : IPlugin
{
    protected abstract DoInitialize ();
    protected abstract DoStuff ();

    void IPlugin.Initialize { this.DoInitialize (); }
    void IPlugin.Stuff { this.DoStuff (); }
}

Здесь есть хороший пост на эту тему:Дизайн API плагина

Лично я бы использовал интерфейс, а все детали реализовал бы плагины.

Я думаю, что самое очевидное, что вам следует сделать, это позволить плагину зарегистрировать обратный вызов (делегаты) для отдельных событий, которые он хочет обработать.Именно так работает большинство фреймворков в .net.

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

Например, разделы «События» в любом элементе управления asp.net делают это следующим образом:Посмотри на Пользовательский контроль

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top