Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 7 months ago.
The community reviewed whether to reopen this question 7 months ago and left it closed:
Original close reason(s) were not resolved
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
接受
或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
发布评论
评论(9)
我见过的最好的插件架构之一是在 Eclipse 中实现的。一切都是插件,而不是具有插件模型的应用程序。基础应用程序本身就是插件框架。
http://www.eclipse.org/articles/Article-Plug -in-architecture/plugin_architecture.html
One of the best plug-in architectures that I have seen is implemented in Eclipse. Instead of having an application with a plug-in model, everything is a plug-in. The base application itself is the plug-in framework.
http://www.eclipse.org/articles/Article-Plug-in-architecture/plugin_architecture.html
我将描述我过去使用过的一种相当简单的技术。此方法使用 C# 反射来帮助插件加载过程。可以修改此技术,使其适用于 C++,但您会失去使用反射的便利性。
IPlugin
接口用于标识实现插件的类。方法被添加到接口中以允许应用程序与插件进行通信。例如,应用程序将使用Init
方法来指示插件初始化。为了查找插件,应用程序会扫描插件文件夹中的 .Net 程序集。每个程序集均已加载。反射用于扫描实现
IPlugin
的类。创建每个插件类的实例。(或者,Xml 文件可能会列出要加载的程序集和类。这可能有助于性能,但我从未发现性能问题)。
每个插件对象都会调用
Init
方法。它传递一个对实现应用程序接口的对象的引用:IApplication
(或特定于您的应用程序的其他名称,例如 ITextEditorApplication)。IApplication
包含允许插件与应用程序通信的方法。例如,如果您正在编写一个文本编辑器,则此界面将具有一个 OpenDocuments 属性,该属性允许插件枚举当前打开的文档的集合。该插件系统可以通过创建派生插件类(例如将
IPlugin
函数和应用程序接口转发到Lua 脚本的LuaPlugin
)来扩展到脚本语言(例如Lua)。该技术允许您在开发过程中迭代地实现
IPlugin
、IApplication
和其他特定于应用程序的接口。当应用程序完成并很好地重构后,您可以记录您公开的接口,并且您应该拥有一个很好的系统,用户可以为其编写自己的插件。I'll describe a fairly simple technique that I have use in the past. This approach uses C# reflection to help in the plugin loading process. This technique can be modified so it is applicable to C++ but you lose the convenience of being able to use reflection.
An
IPlugin
interface is used to identify classes that implement plugins. Methods are added to the interface to allow the application to communicate with the plugin. For example theInit
method that the application will use to instruct the plugin to initialize.To find plugins the application scans a plugin folder for .Net assemblies. Each assembly is loaded. Reflection is used to scan for classes that implement
IPlugin
. An instance of each plugin class is created.(Alternatively, an Xml file might list the assemblies and classes to load. This might help performance but I never found an issue with performance).
The
Init
method is called for each plugin object. It is passed a reference to an object that implements the application interface:IApplication
(or something else named specific to your app, eg ITextEditorApplication).IApplication
contains methods that allows the plugin to communicate with the application. For instance if you are writing a text editor this interface would have anOpenDocuments
property that allows plugins to enumerate the collection of currently open documents.This plugin system can be extended to scripting languages, eg Lua, by creating a derived plugin class, eg
LuaPlugin
that forwardsIPlugin
functions and the application interface to a Lua script.This technique allows you to iteratively implement your
IPlugin
,IApplication
and other application-specific interfaces during development. When the application is complete and nicely refactored you can document your exposed interfaces and you should have a nice system for which users can write their own plugins.我曾经参与过一个项目,每个客户都必须以非常灵活的方式设置系统,我们发现的唯一好的设计就是为客户提供一个 C# 编译器!
如果规范中充满了诸如:
询问很多关于如何支持系统的问题(以及如何收取支持费用,因为每个客户都会认为他们的情况是正常情况,不需要任何支持)插件。),根据我的经验
I once worked on a project that had to be so flexible in the way each customer could setup the system, which the only good design we found was to ship the customer a C# compiler!
If the spec is filled with words like:
Ask lots of questions about how you will support the system (and how support will be charged for, as each customer will think their case is the normal case and should not need any plug-ins.), as in my experience
通常我使用MEF。托管扩展性框架(简称 MEF)简化了可扩展应用程序的创建。 MEF 提供发现和组合功能,您可以利用这些功能来加载应用程序扩展。
如果您有兴趣,请阅读更多...
Usualy I use MEF. The Managed Extensibility Framework (or MEF for short) simplifies the creation of extensible applications. MEF offers discovery and composition capabilities that you can leverage to load application extensions.
If you are interested read more...
根据我的经验,创建灵活的插件架构的两种最佳方法是脚本语言和库。这两个概念在我看来是正交的;两者可以以任何比例混合,就像函数式编程和面向对象编程一样,但在平衡时会发现它们的最大优点。库通常负责实现具有动态功能的特定接口,而脚本则倾向于强调具有动态接口的功能。
我发现基于管理库的脚本的架构似乎效果最好。脚本语言允许对较低级别的库进行高级操作,因此这些库摆脱了任何特定的接口,将所有应用程序级交互留在脚本系统更灵活的手中。
为此,脚本系统必须具有相当强大的 API,具有与应用程序数据、逻辑和 GUI 的挂钩,以及从库导入和执行代码的基本功能。此外,脚本通常需要安全,因为应用程序可以从编写得不好的脚本中正常恢复。使用脚本系统作为间接层意味着应用程序可以在出现 Something Bad™ 的情况下更轻松地自行分离。
打包插件的方式很大程度上取决于个人喜好,但使用简单界面的压缩存档永远不会出错,比如根目录中的
PluginName.ext
。In my experience, the two best ways to create a flexible plugin architecture are scripting languages and libraries. These two concepts are in my mind orthogonal; the two can be mixed in any proportion, rather like functional and object-oriented programming, but find their greatest strengths when balanced. A library is typically responsible for fulfilling a specific interface with dynamic functionality, whereas scripts tend to emphasise functionality with a dynamic interface.
I have found that an architecture based on scripts managing libraries seems to work the best. The scripting language allows high-level manipulation of lower-level libraries, and the libraries are thus freed from any specific interface, leaving all of the application-level interaction in the more flexible hands of the scripting system.
For this to work, the scripting system must have a fairly robust API, with hooks to the application data, logic, and GUI, as well as the base functionality of importing and executing code from libraries. Further, scripts are usually required to be safe in the sense that the application can gracefully recover from a poorly-written script. Using a scripting system as a layer of indirection means that the application can more easily detach itself in case of Something Bad™.
The means of packaging plugins depends largely on personal preference, but you can never go wrong with a compressed archive with a simple interface, say
PluginName.ext
in the root directory.我认为你需要首先回答这个问题:“哪些组件应该是插件?”
您希望将此数字保持在绝对最小值,否则您必须测试的组合数量会爆炸。尝试将您的核心产品(不应具有太多灵活性)与插件功能分开。
我发现 IOC(控制反转)主体(请阅读 springframework)非常适合提供灵活的基础,您可以添加专门化以使插件开发更简单。
I think you need to first answer the question: "What components are expected to be plugins?"
You want to keep this number to an absolute minimum or the number of combinations which you must test explodes. Try to separate your core product (which should not have too much flexibility) from plugin functionality.
I've found that the IOC (Inversion of Control) principal (read springframework) works well for providing a flexible base, which you can add specialization to to make plugin development simpler.
插件模式是一种软件模式,用于通过干净的接口扩展类的行为。通常,类的行为是通过类继承来扩展的,其中派生类会覆盖该类的一些虚拟方法。该解决方案的一个问题是它与实现隐藏相冲突。它还会导致派生类成为不相关行为扩展的聚集地的情况。此外,如上所述,脚本用于实现此模式,“将内部结构作为脚本语言并用该语言编写所有顶级内容。这使得它非常可修改并且实际上面向未来”。库使用脚本管理库。脚本语言允许对较低级别的库进行高级操作。 (也如上面提到的)
The Plug-in Pattern is a software pattern for extending the behaviour of a class with a clean interface. Often behaviour of classes is extended by class inheritance, where the derived class overwrites some of the virtual methods of the class. A problem with this solution is that it conflicts with implementation hiding. It also leads to situations where derived class become a gathering places of unrelated behaviour extensions. Also, scripting is used to implement this pattern as mentioned above "Make internals as a scripting language and write all the top level stuff in that language. This makes it quite modifiable and practically future proof". Libraries use script managing libraries. The scripting language allows high-level manipulation of lower level libraries. (Also as mentioned above)
这并不是一个答案,而是一堆可能有用的评论/示例。
使应用程序可扩展的一种有效方法是将其内部公开为脚本语言,并用该语言编写所有顶级内容。这使得它非常可修改并且实际上是面向未来的(如果您的原语经过精心选择和实现)。 Emacs 就是此类事物的成功案例。与 Eclipse 风格的插件系统相比,我更喜欢它,因为如果我想扩展功能,我不必学习 API 并编写/编译单独的插件。我可以在当前缓冲区本身中编写一个 3 行片段,对其进行评估并使用它。非常平滑的学习曲线和非常令人满意的结果。
我稍微扩展过的一个应用程序是 Trac。它具有组件架构,在这种情况下意味着任务被委托给通告扩展点的模块。然后,您可以实现适合这些点的其他组件并更改流程。这有点像上面 Kalkie 的建议。
另一个不错的是py.test。它遵循“最好的 API 就是没有 API”的理念,并且完全依赖于在每个级别调用的钩子。您可以在根据约定命名的文件/函数中覆盖这些挂钩并更改行为。您可以查看网站上的插件列表,了解它们的实施速度/容易程度。
几点一般性的观点。
This is not an answer as much as a bunch of potentially useful remarks/examples.
One effective way to make your application extensible is to expose its internals as a scripting language and write all the top level stuff in that language. This makes it quite modifiable and practically future proof (if your primitives are well chosen and implemented). A success story of this kind of thing is Emacs. I prefer this to the eclipse style plugin system because if I want to extend functionality, I don't have to learn the API and write/compile a separate plugin. I can write a 3 line snippet in the current buffer itself, evaluate it and use it. Very smooth learning curve and very pleasing results.
One application which I've extended a little is Trac. It has a component architecture which in this situation means that tasks are delegated to modules that advertise extension points. You can then implement other components which would fit into these points and change the flow. It's a little like Kalkie's suggestion above.
Another one that's good is py.test. It follows the "best API is no API" philosophy and relies purely on hooks being called at every level. You can override these hooks in files/functions named according to a convention and alter the behaviour. You can see the list of plugins on the site to see how quickly/easily they can be implemented.
A few general points.
根据我的经验,我发现实际上有两种类型的插件架构。
狭窄的API
,因为插件将填充特定的功能。换句话说,一个允许插件访问您的应用程序,而另一个允许您的应用程序访问插件。
区别很微妙,有时没有区别……您的应用程序需要两者。
我对 Eclipse/将您的应用程序开放到插件模型没有太多经验(Kalkie 帖子中的文章很棒)。我读过一些关于 eclipse 做事方式的内容,但仅此而已。
Yegge 的 属性博客 讨论了如何使用属性模式允许插件和可扩展性。
我所做的大部分工作都使用插件架构来允许我的应用程序访问插件,例如时间/显示/地图数据等。
几年前,我会创建工厂、插件管理器和配置文件来管理所有这些,让我确定在运行时使用哪个插件。
DI 框架
来完成大部分工作。我仍然需要编写适配器来使用第三方库,但它们通常并没有那么糟糕。
In my experience I've found there are really two types of plug-in Architectures.
Eclipse model
which is meant to allow for freedom and is open-ended.narrow API
because the plugin will fill a specific function.To state this in a different way, one allows plugins to access your application while the other allows your application to access plugins.
The distinction is subtle, and sometimes there is no distiction... you want both for your application.
I do not have a ton of experience with Eclipse/Opening up your App to plugins model (the article in Kalkie's post is great). I've read a bit on the way eclipse does things, but nothing more than that.
Yegge's properties blog talks a bit about how the use of the properties pattern allows for plugins and extensibility.
Most of the work I've done has used a plugin architecture to allow my app to access plugins, things like time/display/map data, etc.
Years ago I would create factories, plugin managers and config files to manage all of it and let me determine which plugin to use at runtime.
DI framework
do most of that work.I still have to write adapters to use third party libraries, but they usually aren't that bad.