3

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

为什么我要选择接口而不是基插件类来继承?你能告诉我应该选择哪种设计,为什么?

4

6 回答 6

5

您应该考虑查看 System.Addin (MAF) 命名空间或托管可扩展性框架 (MEF)。MEF 将是首选,即使它仍然是预发布版本,因为它比 MAF 简单得多。这些选择中的任何一个都简化了您需要执行的大量管道工作,以便加载加载项并与它们进行交互。

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

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

于 2009-02-12T21:13:27.343 回答
4

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

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

于 2009-02-12T20:40:06.017 回答
4

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

您可以将插件必须遵循的合同定义为随处引用的接口。

另外,您还可以定义一个实现该接口的基类,但只有一些抽象方法会被一些您无法为其提供默认实现的接口函数调用。

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 (); }
}
于 2009-02-12T20:48:23.050 回答
2

这里有一篇关于这个主题的好帖子: 插件 API 设计

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

于 2009-02-12T20:39:56.423 回答
1

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

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

例如,任何给定的 asp.net 控件中的“事件”部分都是这样做的:看看UserControl

于 2009-02-12T20:40:18.280 回答
1

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

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

我个人的建议是采用一种类似于scwagner建议的方法,并创建一个基类和一个接口。并包括对接口用户的警告,说明未来的版本可能会破坏其实现,并且建议使用基类继承策略以避免未来的兼容性问题。

于 2011-09-21T16:26:34.723 回答