Добавление поддержки плагинов: интерфейс или базовый класс для наследования? - PullRequest
3 голосов
/ 12 февраля 2009

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

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

Ответы [ 6 ]

5 голосов
/ 13 февраля 2009

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

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

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

4 голосов
/ 12 февраля 2009

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

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

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

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 (); }
}
4 голосов
/ 12 февраля 2009

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

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

2 голосов
/ 12 февраля 2009

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

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

1 голос
/ 21 сентября 2011

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

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

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

1 голос
/ 12 февраля 2009

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

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

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

...