如何创建灵活的插件架构?

在我的开发工作中,一个重复的主题是使用或创建内部插件体系结构。我见过它以多种方式处理配置文件(XML,。Conf 等)、继承框架、数据库信息、库等。根据我的经验:

  • 数据库不是存储配置信息的好地方,特别是与数据混合在一起的时候
  • 尝试使用继承层次结构需要了解要编码的插件,这意味着插件体系结构并不完全是动态的
  • 配置文件可以很好地提供简单的信息,但不能处理更复杂的行为
  • 图书馆似乎工作得很好,但是必须小心地创建单向依赖关系。

在我寻求从我所使用的各种架构中学习的同时,我也在向社区寻求建议。您是如何实现 SOLID 插件架构的?你最大的失败是什么(或者你见过的最大的失败) ?如果要实现一个新的插件体系结构,您会怎么做?什么样的 SDK 或者开放源码项目拥有一个好的架构的最好的例子?

以下是我自己发现的一些例子:

这些例子似乎发挥了不同的语言优势。一个好的插件架构必须与语言相关联吗?使用工具创建插件架构是最好的选择,还是根据自己的模型来创建?

72550 次浏览

通常我用 MEF。托管扩展性框架(简称 MEF)简化了可扩展应用程序的创建。MEF 提供了发现和组合功能,您可以利用这些功能来加载应用程序扩展。

如果你感兴趣,请阅读 更多..。

我所见过的最好的插件架构之一是在 Eclipse 中实现的。没有带有插件模型的应用程序,一切都是插件。基本应用程序本身就是插件框架。

Http://www.eclipse.org/articles/article-plug-in-architecture/plugin_architecture.html

这不是一个 回答,而是一堆潜在有用的注释/例子。

  • 使应用程序可扩展的一个有效方法是将其内部脚本语言公开,并用该语言编写所有顶级内容。这使得它具有很强的可修改性和实际的未来证明(如果您的原语选择得当并得到了很好的实现)。Emacs 就是一个成功的例子。我更喜欢这个 Eclipse 风格的插件系统,因为如果我想扩展功能,我不必学习 API 和编写/编译一个单独的插件。我可以在当前缓冲区本身中编写一个3行代码片段,评估它并使用它。非常顺利的学习曲线和非常令人满意的结果。

  • 我稍微扩展了一下的一个应用程序是 Trac。它有一个组件体系结构,在这种情况下,这意味着任务被委托给宣传扩展点的模块。然后,您可以实现其他适合这些点的组件,并更改流程。这有点像上面 Kalkie 的建议。

  • 另一个好的是 Py.test。它遵循“最好的 API 不是 API”的哲学,并且完全依赖于在每个级别调用的钩子。您可以在根据约定命名的文件/函数中覆盖这些钩子并更改行为。您可以查看站点上的插件列表,以了解它们的实现速度和容易程度。

一些大概的观点。

  • 尽量保持不可扩展/不可用户修改的内核尽可能小。将所有可能的事情委托给一个更高的层,这样可扩展性就会增加。核心部分需要纠正的东西要少一些,以防出现错误的选择。
  • 与上述观点相关的是,你不应该在一开始就对项目的方向做出太多的决定。实现所需的最小子集,然后开始编写插件。
  • 如果你正在嵌入一个脚本语言,确保它是一个完整的,你可以用它来编写通用程序,而不是为你的应用程序编写一个玩具语言 只是
  • 尽可能减少样板文件。不要为子类化、复杂的 API、插件注册之类的事情操心。尽量使它保持简单,这样它就是 放松,而不仅仅是 有可能来扩展。这将使您的插件 API 得到更多的使用,并鼓励最终用户编写插件。不仅仅是插件开发者。Test 在这方面做得很好。据我所知是日食 没有

根据我的经验,我发现实际上有两种类型的插件架构。

  1. 一个遵循 Eclipse model,这意味着允许自由,是开放式的。
  2. 另一种通常要求插件遵循 narrow API,因为插件会填充特定的函数。

要以不同的方式表示这一点,一个 允许插件访问您的应用程序而另一个 允许你的应用程序访问插件

区别是微妙的,有时没有区别... 您希望两者都适用于您的应用程序。

我没有大量的 Eclipse/打开你的 App to plugins 模型的经验(Kalkie 的文章很棒)。我读过一些关于日食的报道,但仅此而已。

Yegge 的 房地产博客谈到了属性模式的使用如何支持插件和扩展性。

我所做的大部分工作都是使用插件架构来允许我的应用程序访问插件,比如时间/显示/地图数据等等。

几年前,我会创建工厂、插件管理器和配置文件来管理所有这些,并让我决定在运行时使用哪个插件。

  • 现在,我通常只有一个 DI framework做大部分的工作。

我仍然需要编写适配器来使用第三方库,但它们通常没有那么糟糕。

我曾经在一个项目中工作,这个项目必须非常灵活,每个客户都可以设置系统,我们发现唯一好的设计是给客户发送一个 C # 编译器!

如果说明书中充满了这样的字眼:

  • 灵活
  • 插件
  • 可定制

询问大量关于如何支持系统的问题(以及支持将如何收费,因为每个客户都会认为他们的情况是正常的,不应该需要任何插件)根据我的经验

客户的支持(或 (支持字体的人)写作 插件比 建筑学

我认为您需要首先回答这个问题: “哪些组件应该是插件?” 您希望将此数字保持在绝对最小值,或者保持必须测试爆炸的组合数。尝试将你的核心产品(不应该有太多的灵活性)从插件功能中分离出来。

我发现 IOC (控制反转)原则(即 springFramework)可以很好地提供一个灵活的基础,你可以添加专门化来使插件开发更简单。

  • 您可以扫描容器中的“作为插件类型广告的接口”机制。
  • 您可以使用容器注入插件可能需要的公共依赖项(即 ResourceLoaderAware 或 MessageSourceAware)。

根据我的经验,创建灵活的插件架构的两种最佳方式是脚本语言和库。在我看来,这两个概念是正交的,它们可以按任何比例混合,就像功能性和面向对象程序设计性一样,但在平衡的时候,会发现它们的最大优势。库通常负责使用动态功能实现特定的 接口,而脚本倾向于使用动态接口强调 功能

我发现基于 管理库的脚本的架构似乎工作得最好。这个脚本语言允许对低级库进行高级操作,因此这些库可以从任何特定的界面中解放出来,将所有应用程序级别的交互留给更灵活的脚本系统。

为了实现这一点,脚本系统必须有一个相当健壮的 API,包括应用程序数据、逻辑和 GUI 的挂钩,以及从库导入和执行代码的基本功能。此外,脚本通常需要是 安全,因为应用程序可以从编写不良的脚本中优雅地恢复。使用脚本系统作为间接层意味着应用程序可以更容易地在出现不好的情况时分离自己。

打包插件的方法在很大程度上取决于个人喜好,但是使用一个简单接口的压缩存档(比如根目录中的 PluginName.ext)永远不会出错。

我将描述我过去使用过的一个相当简单的技术。这种方法使用 C # 反射来帮助加载插件。这种技术可以进行修改,使其适用于 C + + ,但是您将失去使用反射的便利。

IPlugin接口用于标识实现插件的类。方法被添加到接口以允许应用程序与插件通信。例如,应用程序将用来指示插件初始化的 Init方法。

要查找插件,应用程序需要扫描插件文件夹。净装配。每个程序集都已加载。反射用于扫描实现 IPlugin的类。创建每个插件类的实例。

(或者,XML 文件可能列出要加载的程序集和类。这可能有助于提高性能,但我从未发现性能方面的问题)。

为每个插件对象调用 Init方法。它被传递一个对实现应用程序接口的对象的引用: IApplication(或特定于您的应用程序的其他名称,例如 ITextEditorApplication)。

IApplication包含允许插件与应用程序通信的方法。例如,如果您正在编写一个文本编辑器,这个接口将有一个 OpenDocuments属性,允许插件枚举当前打开的文档集合。

这个插件系统可以扩展到脚本语言,例如 Lua,通过创建一个派生的插件类,例如 LuaPlugin,它将 IPlugin函数和应用程序接口转发到 Lua 脚本。

这种技术允许您在开发期间迭代地实现 IPluginIApplication和其他特定于应用程序的接口。当应用程序完成并进行了很好的重构时,您可以记录您公开的接口,并且您应该有一个很好的系统,用户可以为其编写自己的插件。

插件模式是一种软件模式,用于扩展具有干净接口的类的行为。类的行为通常是通过类继承来扩展的,其中派生类覆盖了类的一些虚方法。这个解决方案的一个问题是它与实现隐藏冲突。它还会导致派生类成为不相关行为扩展的聚集地。此外,脚本用于实现上面提到的模式“将内部脚本语言作为一个工具,并用该语言编写所有顶级的东西。这使得它具有很强的可修改性和实用的未来证明”。库使用脚本管理库。这个脚本语言允许对低级别的库进行高级别的操作。(亦如上文所述)