【问题标题】:Adding Plugin Support : Interface or Base class to inherit?添加插件支持:要继承的接口或基类?
【发布时间】:2010-10-07 06:51:39
【问题描述】:

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

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

【问题讨论】:

    标签: .net inheritance plugins interface extensibility


    【解决方案1】:

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

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

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

    【讨论】:

    • 这听起来很有趣你知道有什么好的文章可以快速启动它吗?因为现在当我看它时,它看起来非常复杂:)
    • 我认为这是一个好的开始:codeplex.com/MEF/Wiki/… - 我会进一步研究,感谢您的回答。
    • 我刚刚实现了这个,它比我想象的要容易得多(除了我在第一次尝试中犯了一些愚蠢的错误:))
    • @Slough:很高兴你能够让一切正常工作。是的,MEF 让向应用程序添加插件支持变得非常容易。
    • 另一个选项是 Mono.Addins。如果您需要更高级的加载项处理功能,例如加载项依赖项和元数据(请参阅monoaddins.codeplex.com),这是一个不错的选择。
    【解决方案2】:

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

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

    【讨论】:

    • 哇,多重继承的限制是一个我从未想过的好点,为此欢呼。
    【解决方案3】:

    为什么必须是其中一个?

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

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

    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 (); }
    }
    

    【讨论】:

    • 为什么要增加这个 PluginBase 类的额外复杂性/开销?对我来说似乎很没用,而且容易出错。
    • @strager 最初我是这么想的,所以我可以简单地为该基类提供一些额外的标准功能。
    • 啊,好的,我现在明白了。不过,这里的示例并不能很好地说明这一点。
    • 不,这个例子很糟糕,因为它根本没有显示任何默认功能,抱歉。
    【解决方案4】:

    这里有一篇关于这个主题的好帖子: Plugin API design

    我会亲自使用接口,让插件实现所有细节。

    【讨论】:

    • @Jon Tackabury:我们仍然在接口、抽象还是属性上摇摆不定;但是,插件(读取最终用户)将必须实现所有细节。
    【解决方案5】:

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

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

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

    【讨论】:

    • 与接口或继承相比,这有什么优势吗?因为这听起来比我实现一个接口要简单一些。
    • 我稍作编辑。我可能没有完全理解这里,但我所说的是在 .net 中做这件事的标准方式。
    • +1 用于插件注册回调 - cha-ching - 我已经开发了一个允许异步委托注册的类。
    【解决方案6】:

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

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

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

    【讨论】:

      猜你喜欢
      • 2017-07-18
      • 1970-01-01
      • 1970-01-01
      • 2021-11-18
      • 2010-10-21
      • 1970-01-01
      • 2011-03-27
      • 1970-01-01
      • 2011-05-06
      相关资源
      最近更新 更多