添加插件支持:继承接口或基类?

时间:2022-09-24 23:33:21

I'm adding plugin support to my .NET application. A base class sounds reasonable to me, so people can inherit it and override certain calls as well can keep the default functionality or can use some internal helper functions.

我正在为我的.NET应用程序添加插件支持。基类对我来说听起来很合理,所以人们可以继承它并覆盖某些调用,也可以保留默认功能或者可以使用一些内部帮助函数。

Why would I choose interface instead of a base plugin class to inherit? Could you tell which design I should choose and why?

为什么我要选择接口而不是基础插件类来继承?你能说出我应该选择哪种设计以及为什么?

6 个解决方案

#1


You should consider taking a look at the System.Addin (MAF) namespace or the Managed Extensibility Framework (MEF). MEF would be the preferred choice even though it is still pre-release as it is much simpler than MAF. Either one of those choices simplify a lot of the plumbing work you will need to do in order to get add-ins loaded and interact with them.

您应该考虑查看System.Addin(MAF)命名空间或Managed Extensibility Framework(MEF)。 MEF将是首选,即使它仍然是预发布的,因为它比MAF简单得多。这些选择中的任何一个都可以简化许多管道工作,以便加载加载项并与之交互。

As far as making a choice between an interface and an abstract class, if you go with MAF or MEF some of that will be made for you. The most basic differences in this context are that by providing an abstract base class you provide an inheritance point for custom plugins (written either by you or other developers) and ensure that certain methods/properties provide default behavior and force the derived classes to implement certain required methods/properties. The drawback is that you are limited to a single base class.

至于在接口和抽象类之间做出选择,如果你使用MAF或MEF,那么将为你做一些。这个上下文中最基本的区别是,通过提供一个抽象基类,您可以为自定义插件(由您或其他开发人员编写)提供继承点,并确保某些方法/属性提供默认行为并强制派生类实现某些所需的方法/属性。缺点是您只能使用一个基类。

Interfaces get around the single base class limitation and still provide an inhertiance point for custom plugins and ensure that certain methods/properties are implemented but can't provide any type of default behavior. You also cannot specify anything other than public members in an interface while an abstract class can have abstract or virtual non-public members (typically protected).

接口绕过单个基类限制,仍然为自定义插件提供一个入侵点,并确保实现某些方法/属性,但不能提供任何类型的默认行为。您还不能在接口中指定除公共成员之外的任何内容,而抽象类可以具有抽象或虚拟非公共成员(通常受保护)。

#2


Use an interface for the plugin contract. If a certain type of plugin likely shares certain functionality, make an abstract base class that implements the interface. Also ensure that your contracts are in an assembly outside of your actual application.

使用插件合同的接口。如果某种类型的插件可能共享某些功能,请创建一个实现该接口的抽象基类。还要确保您的合同处于实际应用程序之外的程序集中。

Keep in mind that in .NET, you don't have multiple inheritance. Requiring implementers to give up their inheritance slot to provide a little functionality for them is not a good idea.

请记住,在.NET中,您没有多重继承。要求实施者放弃他们的继承槽以为他们提供一些功能并不是一个好主意。

#3


Why does it have to be one or the other?

为什么它必须是一个或另一个?

You can define the contract that the plugins must follow as an interface which is being referenced everywhere.

您可以定义插件必须遵循的合同作为在任何地方引用的接口。

Plus, you can also define a base class which implements that interface, but merely has abstract methods that get called for some of the interface functions that you can't provide a default implementation for.

此外,您还可以定义实现该接口的基类,但仅具有为某些无法提供默认实现的接口函数调用的抽象方法。

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


There is a good post on this topic here: Plugin API design

这里有一个关于这个主题的好文章:插件API设计

I would personally use an interface, and let the plugins implement all the details.

我个人会使用一个接口,让插件实现所有细节。

#5


I think the obvious thing you should do is to let the plugin register callback (delegates) for the individual events it wants to handle. This is the way most frameworks work in .net

我认为你应该做的显而易见的事情是让插件为它想要处理的各个事件注册回调(委托)。这是大多数框架在.net中工作的方式

In other words, neither base classes nor interfaces. The advantage of this solution is that your plugin does not have to conform to any given external interface or base class, it simply registers for the functionality it needs.

换句话说,既不是基类也不是接口。此解决方案的优点是您的插件不必符合任何给定的外部接口或基类,它只需注册它所需的功能。

As an example, the "Events" sections in any given asp.net control does it this way: Have a look at UserControl

举个例子,任何给定的asp.net控件中的“Events”部分就是这样做的:看看UserControl

#6


Something that everyone is forgetting to mention is backwards compatibility. You can add new methods to a base class without breaking existing clients while you cannot modify existing interfaces without affecting them.

每个人都忘记提及的是向后兼容性。您可以在不破坏现有客户端的情况下向基类添加新方法,而无法在不影响现有接口的情况下修改现有接口。

There are alternatives to avoid compatibility issues with interfaces. For example, you could create an new interface that inherits from the old one when you include new extensions for your application. But this has the drawback of bloating your architecture with types such as IClientInterfaceV2 and IClientInterfaceV3.

有一些替代方法可以避免接口的兼容性问题。例如,当您为应用程序包含新扩展时,可以创建一个继承旧接口的新接口。但是这样做的缺点是使用IClientInterfaceV2和IClientInterfaceV3等类型来扩展您的体系结构。

My personal suggestion would be to go with an approach like the one suggested by scwagner and create both a base class and an interface. And include a warning to the interface users stating that future versions might break their implementation and that the base class inheritance strategy is the recommended one to avoid future compatibility issues.

我个人的建议是采用类似scwagner建议的方法,并创建基类和接口。并向接口用户发出警告,指出未来版本可能会破坏其实现,并且建议使用基类继承策略以避免将来出现兼容性问题。

#1


You should consider taking a look at the System.Addin (MAF) namespace or the Managed Extensibility Framework (MEF). MEF would be the preferred choice even though it is still pre-release as it is much simpler than MAF. Either one of those choices simplify a lot of the plumbing work you will need to do in order to get add-ins loaded and interact with them.

您应该考虑查看System.Addin(MAF)命名空间或Managed Extensibility Framework(MEF)。 MEF将是首选,即使它仍然是预发布的,因为它比MAF简单得多。这些选择中的任何一个都可以简化许多管道工作,以便加载加载项并与之交互。

As far as making a choice between an interface and an abstract class, if you go with MAF or MEF some of that will be made for you. The most basic differences in this context are that by providing an abstract base class you provide an inheritance point for custom plugins (written either by you or other developers) and ensure that certain methods/properties provide default behavior and force the derived classes to implement certain required methods/properties. The drawback is that you are limited to a single base class.

至于在接口和抽象类之间做出选择,如果你使用MAF或MEF,那么将为你做一些。这个上下文中最基本的区别是,通过提供一个抽象基类,您可以为自定义插件(由您或其他开发人员编写)提供继承点,并确保某些方法/属性提供默认行为并强制派生类实现某些所需的方法/属性。缺点是您只能使用一个基类。

Interfaces get around the single base class limitation and still provide an inhertiance point for custom plugins and ensure that certain methods/properties are implemented but can't provide any type of default behavior. You also cannot specify anything other than public members in an interface while an abstract class can have abstract or virtual non-public members (typically protected).

接口绕过单个基类限制,仍然为自定义插件提供一个入侵点,并确保实现某些方法/属性,但不能提供任何类型的默认行为。您还不能在接口中指定除公共成员之外的任何内容,而抽象类可以具有抽象或虚拟非公共成员(通常受保护)。

#2


Use an interface for the plugin contract. If a certain type of plugin likely shares certain functionality, make an abstract base class that implements the interface. Also ensure that your contracts are in an assembly outside of your actual application.

使用插件合同的接口。如果某种类型的插件可能共享某些功能,请创建一个实现该接口的抽象基类。还要确保您的合同处于实际应用程序之外的程序集中。

Keep in mind that in .NET, you don't have multiple inheritance. Requiring implementers to give up their inheritance slot to provide a little functionality for them is not a good idea.

请记住,在.NET中,您没有多重继承。要求实施者放弃他们的继承槽以为他们提供一些功能并不是一个好主意。

#3


Why does it have to be one or the other?

为什么它必须是一个或另一个?

You can define the contract that the plugins must follow as an interface which is being referenced everywhere.

您可以定义插件必须遵循的合同作为在任何地方引用的接口。

Plus, you can also define a base class which implements that interface, but merely has abstract methods that get called for some of the interface functions that you can't provide a default implementation for.

此外,您还可以定义实现该接口的基类,但仅具有为某些无法提供默认实现的接口函数调用的抽象方法。

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


There is a good post on this topic here: Plugin API design

这里有一个关于这个主题的好文章:插件API设计

I would personally use an interface, and let the plugins implement all the details.

我个人会使用一个接口,让插件实现所有细节。

#5


I think the obvious thing you should do is to let the plugin register callback (delegates) for the individual events it wants to handle. This is the way most frameworks work in .net

我认为你应该做的显而易见的事情是让插件为它想要处理的各个事件注册回调(委托)。这是大多数框架在.net中工作的方式

In other words, neither base classes nor interfaces. The advantage of this solution is that your plugin does not have to conform to any given external interface or base class, it simply registers for the functionality it needs.

换句话说,既不是基类也不是接口。此解决方案的优点是您的插件不必符合任何给定的外部接口或基类,它只需注册它所需的功能。

As an example, the "Events" sections in any given asp.net control does it this way: Have a look at UserControl

举个例子,任何给定的asp.net控件中的“Events”部分就是这样做的:看看UserControl

#6


Something that everyone is forgetting to mention is backwards compatibility. You can add new methods to a base class without breaking existing clients while you cannot modify existing interfaces without affecting them.

每个人都忘记提及的是向后兼容性。您可以在不破坏现有客户端的情况下向基类添加新方法,而无法在不影响现有接口的情况下修改现有接口。

There are alternatives to avoid compatibility issues with interfaces. For example, you could create an new interface that inherits from the old one when you include new extensions for your application. But this has the drawback of bloating your architecture with types such as IClientInterfaceV2 and IClientInterfaceV3.

有一些替代方法可以避免接口的兼容性问题。例如,当您为应用程序包含新扩展时,可以创建一个继承旧接口的新接口。但是这样做的缺点是使用IClientInterfaceV2和IClientInterfaceV3等类型来扩展您的体系结构。

My personal suggestion would be to go with an approach like the one suggested by scwagner and create both a base class and an interface. And include a warning to the interface users stating that future versions might break their implementation and that the base class inheritance strategy is the recommended one to avoid future compatibility issues.

我个人的建议是采用类似scwagner建议的方法,并创建基类和接口。并向接口用户发出警告,指出未来版本可能会破坏其实现,并且建议使用基类继承策略以避免将来出现兼容性问题。