如何构建基于微内核的插件系统(一)

最近,一直在看.NET版本的开源IDE——SharpDevelop,由几位国外高人编写,第一次接触这么复杂的插件系统,着实感觉无从下手,于是从网上收集了一些有关解读、分析源码的文章,可就是因为太少了,自己只能感悟一点。Eclipse是非常出色的一款Java的开源IDE环境,自然也少不了去了解一下,也大概了解了一下Eclipse的插件系统原理,设计上主要是采用了“微内核+插件”的开发模式。网上有解,关于微内核、巨内核的说法,当然各有优缺点了。SharpDevelop,初看了一下,给人的感觉应该是巨内核的那种,插件的组装全部由系统完成,而不是将任务分发到子插件上的。

为何要提出插件系统?首先,该系统主要是针对提高桌面应用程序的扩展性而提出的;而Web网站本身在设计上就是插件化的,通过用户请求页面,相应调用对应的处理类(就是Page类)进行处理。Web网站,通过增加页面和页面对应的处理类,来实现系统扩展。新添加的页面、对应处理类打包成动态库,这些也正是桌面应用程序所不具备的特点(必须改动原有的项目),这是不利于日后的修改、新增和移除功能的操作,且容易造成影响其他正常模块的正常运行。而插件系统,一旦新增的模块造成系统问题,可以通过移除新模块来快速解决这个问题。虽然Web网站本身是插件化的,但在实际项目开发中,往往有时候把新开发的模块放在已经发布的系统中,源码、页面,生成的动态库都放在一起。一旦新旧项目并行开发,由于各组开发人员的源码的不一致或发布时候的原因,导致已经上线的系统的其它模块出错,那后果不堪设想。所以规范Web开发也是一项重要内容,但这个不在本文讨论的范围内,以后会写一些。

说了一大堆废话,现在言归正传,我先谈谈我对插件系统的理解。打个比方,在菜市场上,菜场的管理人员把菜场大致分了个类,有肉类的、蛋类、蔬菜类、鱼类和水果类,这些就是插件系统中的几个扩展点(Extension)。卖家(插件)带了些蛋、蔬菜,去这个菜场来卖。TA把鸭蛋放在了蛋类的摊位上,把菠菜、青菜放在了蔬菜的摊位上,这样,买家(插件系统内部的调用者)就可以根据分类来找到所需要的了。按实际实现的UI界面元素,来设计主要的几个扩展点,大家都知道,系统与用户进行交互,是通过鼠标点击或键盘的敲击来完成的,也可以说是GUI元素上的事件来驱动的,譬如,菜单上的点击、鼠标右键、按钮的点击等来驱动。我们可以把整个桌面应用程序的GUI界面称之为“工作台(Workbench)”,所有的GUI元素都是在这个上面,大致可分为以下几类:主菜单(MainMenu)、工具栏(Toolbar)、文档选项卡(DocumentTab)、可停靠选项卡(DockingTab)、文件选项卡(OpenFileTab)和状态栏(Statusbar)。还是回到之前菜市场的那个比方中,菜市场管理人员可以提供电子称给需要称的卖家(这种行为就是一种服务形式),并要求卖家签订借用条款(调用服务的接口)。这样,买家买东西时,就可以根据称重和单价来付钱了。有特殊需要就要提供特别的方法,有个卖家,TA发现在这个摊位上特别好卖,所以TA就天天第一个来到菜市场来占用这个摊位了。所以,扩展点除了“工作台(Workbench)”,还需要“服务(Services)”和“自动运行(Autorun)”。插件系统启动时,先初始化“服务(Services)”,然后执行“自动运行(Autorun)”,再将“工作台(Workbench)”下的GUI元素创建出来。通过对GUI元素上的点击事件,相应的执行GUI元素对应的“命令(Command)”对象的“运行(Run)”方法,从而响应鼠标、键盘事件。

插件系统的扩展点可以以路径形式挂载到插件树中,根路径为“/”,像之前提到的扩展点,可以记作“/SharpDevelop/Workbench/MainMenu”、“/SharpDevelop/Services”。如果需要添加一级菜单,譬如是“工具(Tool)”,那么在插件配置中可以这么设置。

<Plugin>
    <Manifest>
        <Identity name="ICSharp.SharpDevelop.OptionDialog" />
    </Manifest>
    <Runtime>
        <Import assembly=":ICSharp.SharpDevelop" />
    </Runtime>
    <Extension path="/SharpDevelop/Workbench/MainMenu/">
        <MenuItem id="Tool" label="工具">
            <MenuItem id="Options" label="选项" class="ICSharp.SharpDevelop.MainMenu.Tool.OptionsCommand" />
        </MenuItem>
    </Extension>
</Plugin>

在Extension下的节点,称之为“代码子(Codon)”,大家都这么翻译,我也不理解为什么这么起名,总之,挂载到扩展点下的就是Codon。一个Codon可以对应一个或零个Command。“MenuItem”就是类型为“MenuItem”的“Codon”。如上边那个插件配置,那个“选项”菜单的Codon,通过用户点击触发事件,从而执行“ICSharp.SharpDevelop.MainMenu.Tool.OptionsCommand”类实例的“Run”方法。Codon的类型有“MenuItem”、“Class”等等,可以根据不同类型的Codon,来编写特殊的抽象类,来提供特殊的功能,如“MenuItem”,可以写一个对应的Command抽象类“AbstractMenuCommand”。

上边简单描述了插件是如何实现扩展功能的,后边我会继续分析插件系统,有关如何加载以及延迟加载的概念。

posted @ 2010-07-15 16:25  Berkaroad  阅读(1299)  评论(0编辑  收藏  举报