我正在为我的.NET应用程序添加插件支持。基类对我来说听起来很合理,所以人们可以继承它,并且覆盖某些调用也可以保留默认功能或者可以使用一些内部帮助函数。
为什么我会选择接口而不是基础插件类来继承?你能说出我应该选择哪种设计以及为什么?
答案 0 :(得分:5)
您应该考虑查看System.Addin(MAF)命名空间或Managed Extensibility Framework(MEF)。 MEF将是首选,即使它仍然是预发布的,因为它比MAF简单得多。这些选择中的任何一个都可以简化许多管道工作,以便加载加载项并与之交互。
至于在界面和抽象类之间做出选择,如果你选择MAF或MEF,那么将会为你做一些。此上下文中最基本的区别在于,通过提供抽象基类,您可以为自定义插件(由您或其他开发人员编写)提供继承点,并确保某些方法/属性提供默认行为并强制派生类实现某些所需的方法/属性。缺点是您只能使用一个基类。
接口绕过单个基类限制,仍然为自定义插件提供了一个inhertiance点,并确保实现某些方法/属性,但不能提供任何类型的默认行为。您还不能在界面中指定除公共成员之外的任何内容,而抽象类可以具有抽象或虚拟非公共成员(通常受保护)。
答案 1 :(得分:4)
使用插件合同的界面。如果某种类型的插件可能共享某些功能,请创建一个实现该接口的抽象基类。还要确保您的合同在实际应用程序之外的程序集中。
请记住,在.NET中,您没有多重继承。 要求实施者放弃继承槽以为他们提供一些功能并不是一个好主意。
答案 2 :(得分: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 (); }
}
答案 3 :(得分:2)
这里有一篇关于这个主题的好文章: Plugin API design
我个人会使用一个界面,让插件实现所有细节。
答案 4 :(得分:1)
我认为你应该做的显而易见的事情就是让插件为它想要处理的各个事件注册回调(委托)。这是大多数框架在.net
中的工作方式换句话说,既不是基类也不是接口。这个解决方案的优点是你的插件不必符合任何给定的外部接口或基类,它只是注册它所需的功能。
例如,任何给定的asp.net控件中的“Events”部分都是这样做的:看看UserControl
答案 5 :(得分:1)
每个人都忘记提及的是向后兼容性。您可以在不破坏现有客户端的情况下向基类添加新方法,同时无法修改现有接口而不会影响它们。
有一些替代方法可以避免接口的兼容性问题。例如,当您为应用程序包含新扩展时,可以创建一个继承旧接口的新接口。但是这样做的缺点是使用IClientInterfaceV2和IClientInterfaceV3等类型来扩展您的体系结构。
我个人的建议是采用类似 scwagner 建议的方法,并创建基类和接口。并向接口用户发出警告,指出未来版本可能会破坏其实现,并且建议使用基类继承策略以避免将来出现兼容性问题。