如何创build一个灵活的插件架构?

在我的开发工作中重复的主题是使用或创build内部插件架构。 我已经看到它接近许多方面 – configuration文件(XML,.conf等),inheritance框架,数据库信息,库和其他。 在我的经验中:

  • 数据库不是存储configuration信息的好地方,尤其是与数据混合在一起
  • 尝试使用inheritance层次结构需要知道插件编码的含义,这意味着插件体系结构并非全是dynamic的
  • configuration文件适用于提供简单的信息,但不能处理更复杂的行为
  • 图书馆似乎运作良好,但单向依赖性必须小心创build。

当我试图从我所使用的各种架构中学习时,我也在向社区寻求build议。 你是如何实现SOLID插件架构的? 你最糟糕的失败是什么(或者你所看到的最糟糕的失败)? 如果你要实现一个新的插件架构,你会做什么? 您曾经使用过的SDK或开源项目是否具有良好架构的最佳范例?

我自己发现的几个例子:

  • Perl的Module :: Plugable和IOC,用于在Perl中进行dependency injection
  • 用于dependency injection的各种Spring框架 (Java,.NET,Python)。
  • 带有Java列表的SO问题 (包括服务提供者接口 )
  • C ++指出Dobbs博士的一篇 SO问题
  • 关于ASP.NET MVC的特定插件思想的SO问题

这些例子似乎发挥了各种语言的优势。 是一个好的插件架构必然与语言绑定? 是最好使用工具来创build一个插件架构,或者在自己的以下模型?

这不是一堆可能有用的评论/例子。

  • 使应用程序可扩展的一个有效方法是将其内部结构作为脚本语言公开,并使用该语言编写所有顶层的东西。 这使得它非常易于修改和实际上将来可以certificate(如果你的原语是精心挑选和实施的话)。 这种事情的成功故事是Emacs。 我更喜欢这个到Eclipse风格的插件系统,因为如果我想扩展function,我不必学习API和编写/编译一个单独的插件。 我可以在当前缓冲区自己写一个3行代码,评估它并使用它。 非常stream畅的学习曲线和非常令人满意的结果。

  • 我已经扩展了一个应用程序是Trac 。 它有一个组件架构,在这种情况下意味着任务被委托给通告扩展点的模块。 然后,您可以实现其他适合这些点的组件,并更改stream程。 这有点像Kalkie的build议。

  • 另一个很好的是py.test 。 它遵循“最好的API不是API”的理念,纯粹依赖于各个层面的钩子。 你可以在根据约定命名的文件/函数中覆盖这些钩子,并改变行为。 您可以在网站上看到插件列表,了解它们可以快速/轻松地实施。

几个一般的观点。

  • 尽量保持您的不可扩展/非用户可修改的核心尽可能小。 将所有可以到达的更高层委派给可扩展性增加。 在核心纠正的东西less,那么在不好的select的情况下。
  • 与上述相关的是,你不应该从一开始就就项目的方向作出太多的决定。 实现最小的需要的子集,然后开始编写插件。
  • 如果您正在embedded脚本语言,请确保它是完整的,您可以在其中编写通用程序,而不是用于应用程序的玩具语言。
  • 尽可能减less样板。 不要打扰子类,复杂的API,插件注册和类似的东西。 尽量保持简单,这样很容易 ,不仅可以扩展。 这将使您的插件API更多地使用,并鼓励最终用户编写插件。 不只是插件开发人员。 py.test做得很好。 据我所知,Eclipse 没有 。

根据我的经验,我发现实际上有两种types的插件体系结构。

  1. 一个遵循Eclipse model ,这是为了自由,是开放的。
  2. 另一个通常需要插件来遵循narrow API因为插件将填充特定的function。

要用不同的方式说明, 可以使用插件来访问你的应用程序,而另一个允许你的应用程序访问插件

这种区别是微妙的,有时候不会有任何的偏见……你希望为你的申请提供这两者。

我没有太多的Eclipse经验/打开你的应用程序插件模型(在Kalkie的文章中的文章是伟大的)。 我已经读了一些日食的方式,但没有比这更多。

Yegge的属性博客谈论了如何使用属性模式允许插件和可扩展性。

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

几年前,我会创build工厂,插件pipe理器和configuration文件来pipe理所有这些,并让我确定在运行时使用哪个插件。

  • 现在我通常只有一个DI framework完成大部分工作。

我仍然需要编写适配器来使用第三方库,但通常并不是那么糟糕。

在Eclipse中实现了我所见过的最好的插件体系结构之一。 所有东西都是插件,而不是插件模型的应用程序。 基本应用程序本身就是插件框架。

http://www.eclipse.org/articles/Article-Plug-in-architecture/plugin_architecture.html

我曾经参与过一个项目,每个客户都可以设置这个系统,这个项目必须非常灵活,我们发现的唯一一个好的devise就是向客户提供一个C#编译器!

如果规范充满了像这样的话:

  • 灵活
  • 插入
  • 定制

询问关于如何支持系统的许多问题(以及如何收取支持,因为每个客户都会认为他们的情况是正常情况,不应该需要任何插件),就像我的经验一样

客户(或者在线支持人员)编写插件的支持比架构难得多

Usualy我使用MEF。 托pipe扩展框架(简称MEF)简化了可扩展应用程序的创build。 MEF提供发现和组合function,您可以利用它们来加载应用程序扩展。

如果你有兴趣阅读更多…

我将描述一个我过去使用的相当简单的技术。 这种方法使用C#reflection来帮助插件加载过程。 这种技术可以修改,所以它适用于C ++,但是你失去了使用reflection的便利。

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

要查找插件,应用程序将扫描.Net程序集的插件文件夹。 每个程序集都被加载。 reflection用于扫描实现IPlugin类。 每个插件类的实例都被创build。

(或者,一个Xml文件可能会列出要加载的程序集和类,这可能有助于性能,但我从来没有发现性能问题)。

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

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

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

这种技术允许您在开发过程中迭代地实现IPluginIApplication和其他特定于应用程序的接口。 当应用程序是完整的,很好地重构,你可以logging你的暴露的接口,你应该有一个很好的系统,用户可以编写自己的插件。

我想你需要先回答这个问题:“什么组件可能是插件? 你想保持这个数字绝对最小或你必须testing组合的数量爆炸。 尝试从插件function中分离您的核心产品(不应该有太多的灵活性)。

我发现IOC(反转控制)主体(读取springframework)对于提供一个灵活的基础很有效,你可以添加专门化来简化插件的开发。

  • 您可以扫描容器的“接口作为插件types广告”机制。
  • 您可以使用容器来注入插件可能需要的常见依赖关系(即ResourceLoaderAware或MessageSourceAware)。

根据我的经验,创build灵活插件体系结构的两种最佳方法是脚本语言和库。 这两个概念在我心中是正交的; 两者可以按任意比例混合,而非function性和面向对象的编程,但在平衡时find最大的优势。 一个库通常负责实现一个具有dynamicfunction的特定接口 ,而脚本倾向于强调dynamic接口的function

我发现基于pipe理库的脚本的体系结构似乎工作得最好。 脚本语言允许对较低级别的库进行高级别的操作,因此这些库可以从任何特定的接口中释放出来,从而使所有的应用程序级交互成为脚本系统更灵活的手段。

为此,脚本系统必须具有一个相当强大的API,包含应用程序数据,逻辑和GUI的钩子,以及从库中导入和执行代码的基本function。 此外,通常要求脚本是安全的,因为应用程序可以从一个写得不好的脚本中正常恢复。 使用脚本系统作为间接层意味着应用程序可以更容易地脱离自己的情况下的东西坏™。

打包插件的方式在很大程度上取决于个人喜好,但是对于一个带有简单接口的压缩归档,绝对不会出错,比如根目录中的PluginName.ext