随着时间的推进,开发人员被推向了快速组件开发和即插即用架构的需求。行业正在寻求企业产品组合的集成,或者某种形式的企业服务总线(ESB)。听过许多业界领袖的演讲,他们试图证明苹果是橘子(或者至少是一种新的红色硬质的橘子,它在内部看起来和感觉就像橘子,但提供了更多的利益)。虽然同意,这样的想法可能来自于某人的严重便秘,或者来自那些自认为是爱因斯坦的大脑。但这些论点也并非完全错误。对于产品的生存来说,变得适应性强、可扩展和多态性是非常重要的,尤其是在针对中小型市场的中小型组织中。
另一个非常有趣的商业需求是将多个业务逻辑流程分隔开,跨越物理上不同的开发团队,然后在同一个装配线上集成它们。即使在这种情况下,使用插件模式进行分离也变得很重要。
在“企业应用组合集成平台”上工作了几个月,每个人都期望插件至少和阿不思·邓布利多一样强大。痛苦地学到了,要在多个可集成的应用程序中统一使用插件管理平台。一开始并没有强制使用它,但一旦考虑插件架构,就要考虑统一它们。
本文针对这样的需求,可以快速启动插件托管、管理和集成。通过使用这样的框架,作为开发人员,只需要编写自定义业务逻辑,而不需要弄脏手。
基于事件的插件交互/调用。时代已经变了。现在,插件被期望至少是一个超人。它需要理解主机应用程序的手势,它需要在后台工作(异步),它需要引发和订阅平台定义的事件,它需要在需要部署新版本时进行更改,它需要与其他调用通道集成。因此,本文提出了基于事件的插件。插件在初始化期间与主机平台握手。这样的握手包括相互订阅感兴趣的事件。这种握手将持续到插件在主机作用域/生命周期内。一些初始化仍然是一个简单的调用,但这些原则上也可以升级为事件。个人的看法是,只有当事件的发生(或其时间框架)不确定时,才需要基于事件的通信。
正如蜘蛛侠所说,“权力伴随着责任”,太小了,无法证明它是错误的。所以,责任从使用Dispatcher对象开始,当在UI密集型应用程序中使用它时(这在大多数情况下都是如此)。
从过去“肮脏的工作”中走出来,变得轻松和洗手。托管插件的一个很好的或重要部分是加载它们。几年前,开发人员不得不跳跃、捕捉和大喊(简而言之,是一场马戏团的努力)来反射对象创建。然后是MEF(http://mef.codeplex.com/)。简而言之,它帮助快速和动态地包装插件实例创建的痛苦。在这里的许多其他实用工具中,这一个被证明是相当的祝福。PluginManager内部使用了一个工厂,它作为整个系统的核心,而核心的第一次跳动看起来像这样:
public void Compose()
{
var catalog = new AggregateCatalog();
catalog.Catalogs.Add(new DirectoryCatalog(extensionsPath));
Directory.GetDirectories(extensionsPath).ToList().ForEach(
p => catalog.Catalogs.Add(new DirectoryCatalog(p)));
container = new CompositionContainer(catalog);
container.ComposeParts(this);
}
有许多东西是免费的,其中最主要的一个是自动从多个不相交的探测路径解析依赖关系。
这个类隐藏在一个简短的接口后面(以防万一/如果/当MEF运行过时的那一天)。
public interface IComposition<T>
{
void Compose();
void Decompose();
IEnumerable<T> ComposedItems { get; set; }
}
有了这些作为样板代码,其余的托管代码就开始了...
术语宿主在这里有点被夸大了,也许容器听起来更合适。但再次,它做的不仅仅是容器。这些几个类负责:加载插件程序集(它可以配置“从哪里” - 使用了用户的%APPDATA%文件夹)作为标准应用程序到插件查询的一站式商店。这里还添加了一个示例WPF窗口,以演示插件平台的主要使用。请注意,示例宿主只做了加载插件的最低限度。还有许多其他功能需要探索(下面有文档)。
以下是宿主的两个最重要的方法:
private bool RegisterPluginHandshake(IEnumerable<PluginInfo> plugins)
{
return pluginMgr.RegisterExtensions(this, PluginNotifyMessage, GetUserContext(), plugins);
}
private void UnRegisterPluginHandshake(IEnumerable<PluginInfo> plugins)
{
pluginMgr.UnLoadExtensions(this, PluginNotifyMessage, plugins);
}
深入插件平台...
这里有一个功能图,展示了平台的范围。提到的接口名称可能与附加代码中的不同。但它应该很容易确定哪些是那些。
以下是管理事件合同的接口。
public interface IApplicationEvents
{
// called when popup Application starts
event EventHandler OnApplicationStart;
// called when popup Application is closed
event EventHandler OnApplicationClose;
// called when the module is first time loaded
event EventHandler OnModuleInitalize;
// called when any module file action
event EventHandler OnFileSystemEvent;
// called when any module action like show window
event EventHandler OnActivateEvent;
}
public interface IExtensionEvents
{
event EventHandler OnStarted;
event EventHandler OnCompleted;
event EventHandler OnError;
}
这里需要注意的是,应用程序和插件本身都是事件源。虽然平台可以自由决定它想要参与的事件类型,但插件仅限于引发平台预先决定要监听的事件。这绝不应该被理解为一个专横的决定或控制媒介。可能有一系列有趣的事件,所有这些事件都可以在多个版本/发布下提供。请查看下面的插图。
框架中的代码是直接的,可以只使用Common.plugin.Platform.dll(除非需要更改框架或调试)。解决方案中包含了两个基本插件,以帮助开始。
KickStartPlugin - 就像名字一样愚蠢,它只是监听Windows资源管理器中的右键单击上下文菜单项事件,并响应另一个并行事件。这是通过单例应用程序实例发生的(代码包括在内,但在插件集成的讨论范围之外)。
TimeEventPlugin - 另一个同样愚蠢的用例。这个插件内部使用一个计时器每10秒引发一次事件,以使应用程序变得混乱(只是为了兴趣)。