如何设计可扩展的软件(插件架构)?

79

我需要一些关于如何设计可扩展软件的资源,即其他人可以编写插件/附加组件来增加其功能。

你有什么建议?有没有讨论这个主题的书籍? 我希望内容简洁明了;一点理论和大量具体示例。

我不针对特定的语言,我想能够理解核心思想,以便在任何语言中实现它。

出于同样的原因,我更倾向于不使用其他人构建的框架(除非框架不是非常高级,即不会隐藏“太多”),目前我只想在这个主题上进行教育和尝试各种实现方式。 此外,框架通常假定用户对该主题有所了解。

更新

我不是在问面向对象编程或允许我的类被继承。 我是说设计一个应用程序,部署在系统上,使得可以在其部署之后通过第三方插件扩展。

例如,Notepad ++具有插件架构,您可以将.dll文件放置在插件文件夹中,它会添加以前不存在的应用程序功能,例如拾色器、代码片段插入或许多其他功能(广泛的功能范围)。


我认为您会发现大多数插件环境都提供用于编写插件的基类。您的自定义第三方插件将从基类派生,并扩展其“插件标准”功能。 - Kieveli
3
你会发现类似Firefox和Notepad++这样的软件具有可扩展性,其根源在于它们的面向对象设计。使你的类可扩展的同样原则也将有助于使整个软件具备可扩展性。 - orokusaki
12个回答

25

1
Spooky,使用C#的插件架构链接是一段代码,看起来与我曾经编写的POC完全相同。唯一缺少的是:一个文件系统监视器,在运行时拾取新模块。非常适合演示:“将dll放入此目录中,然后... Voila!一个新的菜单选项。” - Guge
1
因为我一开始没有注意到这个链接,所以被接受了。 - hasen
谢谢。Asante。Shukria。Shukran。Tenkyu tru。Obligad。Merci。Gracias。Arigato。谢谢。Navazish。 - bugmagnet
非常有趣的东西!谢谢! - Sander Versluys

15

OSGI是一个很好的技术框架实例,可以实现您所需的功能。

相关理论请参考这里

有一本免费书籍提供给您。

可扩展性和编写插件的能力必须处理服务生命周期

  • 现场添加/删除服务/插件
  • 管理服务之间的依赖关系
  • 管理服务的状态(已声明、已安装、已启动、已停止等)

OSGI是用来做什么的?

模块的主要功能之一是作为部署单元…我们可以构建或下载并安装它来扩展应用程序的功能。

这里可以找到关于服务这一核心概念的良好介绍(与您的问题相关,并解释了一些与可扩展性有关的服务问题)。

摘录如下:

如果许多应用程序可以在没有服务的情况下构建,那么为什么服务如此重要?嗯,服务是将软件组件相互解耦的最佳已知方法。

服务最重要的方面之一是它们显着减少类加载问题,因为它们使用对象实例而不是类名。这些实例由提供者创建,而不是消费者创建。复杂度的减少非常惊人。

服务不仅最小化了配置,而且还显着减少了共享包的数量。


什么是OSGi?我看了网站,但我不明白它与我的问题有什么关系! - hasen
2
看看这个:https://dev59.com/THVD5IYBdhLWcg3wDXF3 - victor hugo
在场地上添加/删除服务/插件,只有对于持续运行的服务器类型应用程序才真正有用;其他应用程序可以在启动时加载最新版本的插件。 - Raedwald

6
在您的应用程序中实现SOLID原则。 1. 单一职责原则:一个类应该只有一个职责(即软件规范的唯一变化只能影响类的规范)。 2. 开闭原则:软件实体...应该对扩展开放,对修改关闭3. 里氏替换原则:程序中的对象应该可以用它们的子类型的实例替换,而不会改变该程序的正确性。 4. 接口隔离原则:许多特定于客户端的接口比一个通用接口更好。 5. 依赖倒置原则:应该依赖于抽象。不要依赖具体实现。
Stackoverflow问题: 单一职责原则示例

开闭原则是一个好的想法吗?

里氏替换原则是什么?

接口隔离原则 - 编程到接口

依赖反转原则是什么,为什么它很重要?


Bob的ISP(接口隔离原则)似乎与插件架构相反。插件架构意味着一个或多个通用接口,不是吗? - red0ct

4
你尝试达到了两个相互竞争的目标:
  1. 软件组件必须大量地暴露自身,以便它们可以被重用
  2. 软件组件必须非常少地暴露自身,以便它们可以被重用
解释:为了鼓励代码重用,你应该能够扩展现有类并调用它们的方法。当方法被声明为“私有”时,类被声明为“final”(不能被扩展)时,这是不可能的。因此,为了实现这个目标,一切都应该是公开和可访问的。没有私有数据或方法。
当你发布软件的第二个版本时,你会发现第一个版本的很多想法是错误的。你需要改变很多接口或你的代码、方法名称、删除方法、破坏API。如果你这样做,很多人会离开。因此,为了能够演进你的软件,组件必须不暴露任何不绝对必要的东西 - 以代码重用为代价。
例如:我想观察SWT StyledText中光标(caret)的位置。光标不是用来扩展的。如果你这样做,你会发现代码包含类似“这个类是否在org.eclipse.swt包中”的检查,以及许多方法是私有的、final的等等。我不得不从SWT中复制大约28个类到我的项目中来实现这个功能,因为一切都被锁定了。
SWT是一个很好用但很难扩展的框架。

3

2

这取决于编程语言。

  • 在C/C++中,我相信有一个loadlibrary函数,它允许你在运行时打开一个库并调用其导出函数。这通常是使用C/C++完成的。
  • 在.NET中,有反射(Reflection),它提供类似于loadlibrary的功能(但更广泛)。还有基于反射构建的整个库,如托管扩展框架(Managed Extension Framework)或Mono.Addins,这些库已经为你完成了大部分繁重的工作。
  • 在Java中,也有反射。还有JPF(Java插件框架),它被用于像Eclipse之类的东西中。

根据您使用的编程语言,我可以推荐一些教程/书籍。希望这对您有所帮助。


"loadlibrary":不在标准的C/C++中。 - Raedwald

1

由于我没有足够的声望点来留下评论,所以我将其发布为答案。SharpDevelop是用于开发C#/VB.NET/Boo应用程序的IDE。它具有相当令人印象深刻的架构,允许自己以多种方式进行扩展-从新菜单项到支持全新语言的开发。

它使用一些XML配置作为核心IDE和插件实现之间的粘合层。它可以处理插件的定位、加载和版本控制。部署新插件只需要简单地复制新的xml配置文件和所需的程序集(DLL),然后重新启动应用程序即可。您可以在原始作者Christian Holm、Mike Krüger、Bernhard Spuida的书“Dissecting a csharp application”中了解更多信息,该书介绍了该应用程序的内容here。这本书似乎在该网站上不可用,但我找到了一份可能仍然存在的副本here

还发现了一个相关问题这里

1

插件架构因其可扩展性和灵活性而变得非常流行。

对于C++,Apache httpd服务器实际上是基于插件的,但使用了模块的概念。大多数Apache功能都实现为模块,例如缓存、重写、负载平衡,甚至线程模型。这是我见过的最模块化的软件。

对于Java,Eclipse绝对是基于插件的。Eclipse的核心是一个OSGI模块系统,管理包,另一个插件的概念。包可以提供扩展点,我们可以在此基础上轻松构建模块。OSGI中最复杂的是其动态特性,这意味着包可以在运行时安装或卸载。不再有停止世界的综合症!


0

如果您使用 .Net,我们的研究得出了两种方法:脚本和组合。

脚本

通过使用脚本来编排类的功能扩展,从而扩展它们的功能。这意味着将您最喜欢的 .Net 语言中编译的内容暴露在动态语言中。

我们发现一些值得探索的选项:

组合

如果你使用.NET 4或以上版本开始一个项目,你必须好好研究一下托管可扩展框架(MEF)。它允许你以插件方式扩展应用程序的功能。

托管可扩展框架(MEF)是.NET的组合层,它提高了大型应用程序的灵活性,可维护性和可测试性。 MEF可用于第三方插件的可扩展性,也可以将松散耦合的像插件一样的架构的优点带给普通应用程序。

托管插件框架也是值得一读的好文章。


0
看看微软的“CAB” - 组合应用程序构建块框架。我认为他们也有一个“Web版本”...

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接