我的开发工作中的一个重复主题是使用或创建内部插件架构。我已经看到它涉及多种方式 - 配置文件(XML,.conf等),继承框架,数据库信息,库等。根据我的经验:
当我寻求从我使用的各种架构中学习时,我也希望社区寻求建议。您是如何实现SOLID插件架构的?你最糟糕的失败是什么(或者你看到的最严重的失败)?如果要实现新的插件架构,您会怎么做?您使用过的SDK或开源项目是否具有良好架构的最佳示例?
我自己找到的几个例子:
这些例子似乎发挥了各种语言优势。一个好的插件架构是否必然与语言相关?最好使用工具来创建插件架构,还是在自己的以下模型上进行?
答案 0 :(得分:80)
这不是一个答案,而是一堆可能有用的评论/示例。
使应用程序可扩展的一种有效方法是将其内部结构作为脚本语言公开,并用该语言编写所有顶级内容。这使得它具有很强的可修改性和实际的未来证明(如果您的原语被很好地选择和实现)。这种事情的成功故事是Emacs。我更喜欢eclipse样式插件系统,因为如果我想扩展功能,我不必学习API并编写/编译单独的插件。我可以在当前缓冲区中编写一个3行代码段,对其进行评估并使用它。非常流畅的学习曲线和非常令人愉快的结果。
我扩展了一点的应用是Trac。它具有组件体系结构,在这种情况下意味着将任务委托给广告扩展点的模块。然后,您可以实现适合这些点的其他组件并更改流程。这有点像Kalkie的建议。
另一个好的是py.test。它遵循“最佳API是无API”的理念,完全依赖于在每个级别调用的钩子。您可以在根据约定命名的文件/函数中覆盖这些挂钩并更改行为。您可以在网站上查看插件列表,了解它们的实施速度/速度。
一些一般观点。
答案 1 :(得分:23)
根据我的经验,我发现实际上有两种类型的插件架构。
Eclipse model
,这意味着允许自由并且是开放式的。narrow API
,因为该插件将填充特定功能。要以不同的方式说明,一个允许插件访问您的应用程序,而另一个允许您的应用程序访问插件。
区别是微妙的,有时没有任何干扰......你想要你的应用程序。
我没有大量使用Eclipse的经验/打开你的应用程序插件模型(Kalkie的帖子中的文章很棒)。我已经阅读了eclipse做事的方式,但仅此而已。
Yegge的properties blog谈到了如何使用属性模式允许插件和可扩展性。
我所做的大部分工作都使用插件架构来允许我的应用访问插件,例如时间/显示/地图数据等。
多年前,我会创建工厂,插件管理器和配置文件来管理所有这些,让我确定在运行时使用哪个插件。
DI framework
做大部分工作。我仍然需要编写适配器来使用第三方库,但它们通常并没有那么糟糕。
答案 2 :(得分:14)
我见过的最好的插件架构之一是在Eclipse中实现的。一切都是插件,而不是拥有插件模型的应用程序。基础应用程序本身就是插件框架。
http://www.eclipse.org/articles/Article-Plug-in-architecture/plugin_architecture.html
答案 3 :(得分:8)
我曾经参与过一个项目,每个客户都可以设置系统的方式非常灵活,我们发现的唯一好的设计就是为客户提供一个C#编译器!
如果规范中填写了如下字样:
询问很多关于如何支持系统的问题(以及如何支付费用,因为每个客户都认为他们的案例是正常情况,不需要任何插件。),如我的经验< / p>
客户的支持(或 fount-line支持人们)写作 插件要比插件困难得多 体系结构
答案 4 :(得分:7)
Usualy我使用MEF。 Managed Extensibility Framework(简称MEF)简化了可扩展应用程序的创建。 MEF提供了可用于加载应用程序扩展的发现和组合功能。
如果您有兴趣,请阅读more...
答案 5 :(得分:6)
我将描述一种我过去使用的相当简单的技术。此方法使用C#反射来帮助插件加载过程。可以修改此技术,使其适用于C ++,但是您无法使用反射。
IPlugin
接口用于标识实现插件的类。方法被添加到接口以允许应用程序与插件通信。例如,应用程序将用于指示插件初始化的Init
方法。
要查找插件,应用程序会扫描.Net程序集的插件文件夹。每个组件都已加载。反射用于扫描实现IPlugin
的类。创建每个插件类的实例。
(或者,Xml文件可能会列出要加载的程序集和类。这可能有助于提高性能,但我从未发现性能问题。)
为每个插件对象调用Init
方法。它传递了一个对实现应用程序接口的对象的引用:IApplication
(或其他名称特定于您的应用程序的东西,例如ITextEditorApplication)。
IApplication
包含允许插件与应用程序通信的方法。例如,如果您正在编写文本编辑器,则此接口将具有OpenDocuments
属性,允许插件枚举当前打开的文档的集合。
这个插件系统可以扩展到脚本语言,例如Lua,方法是创建一个派生插件类,例如LuaPlugin
将IPlugin
函数和应用程序接口转发给Lua脚本。
此技术允许您在开发期间迭代地实现IPlugin
,IApplication
和其他特定于应用程序的接口。当应用程序完成并且很好地重构时,您可以记录暴露的接口,并且您应该有一个很好的系统,用户可以编写自己的插件。
答案 6 :(得分:3)
我认为你需要首先回答这个问题:“预期哪些组件是插件?” 您希望将此数字保持为绝对最小值或必须测试的组合数量。尝试将您的核心产品(不应该具有太多的灵活性)与插件功能分开。
我发现IOC(控制反转)主体(读取springframework)非常适合提供灵活的基础,您可以添加专门化以使插件开发更简单。
答案 7 :(得分:3)
根据我的经验,创建灵活插件架构的两种最佳方法是脚本语言和库。这两个概念在我看来是正交的;这两者可以任意比例混合,而不是像功能和面向对象的编程,但在平衡时发现它们最大的优势。库通常负责使用动态功能来实现特定的接口,而脚本则倾向于使用动态接口强调功能。
我发现基于脚本管理库的架构似乎效果最好。脚本语言允许对低级库进行高级操作,因此库可以从任何特定的接口中释放出来,将所有应用程序级别的交互留在脚本系统的更灵活的手中。
为了实现这一点,脚本系统必须具有相当强大的API,具有应用程序数据,逻辑和GUI的钩子,以及从库导入和执行代码的基本功能。此外,脚本通常需要安全,因为应用程序可以从编写得不好的脚本中正常恢复。使用脚本系统作为间接层意味着在Something Bad™的情况下应用程序可以更容易地自行分离。
打包插件的方式在很大程度上取决于个人喜好,但是使用简单界面的压缩存档(例如根目录中的PluginName.ext
)永远不会出错。
答案 8 :(得分:1)
Plug-in Pattern是一种用于通过干净接口扩展类行为的软件模式。通常,类的行为通过类继承来扩展,其中派生的类将覆盖类的某些虚拟方法。该解决方案的一个问题是,它与实现隐藏冲突。这也导致派生类成为不相关行为扩展的聚集地的情况。另外,脚本用于实现上述模式,如上文所述“将内部结构作为脚本语言并用该语言编写所有顶级内容。这使其相当可修改,并在实践中可以证明未来”。库使用脚本管理库。脚本语言允许对低级库进行高级操作。 (也如上所述)