我正在与MEF合作以获得插件架构.我想以一定的可扩展性进行设计.我想扩展初始化.
我所拥有的是一个“驱动程序”,它从某些来源重复收集数据.这些是我的插件.每个插件都需要初始化.现在我有一个接口,这些插件需要实现.
interface IDriverLiveCollection
{
ILiveCollection GetCollection(ILog logger, IDriverConfig config);
}
该接口基本上是从插件创建ILiveCollection的实例.为了更好地理解ILiveCollection看起来像这样.
interface ILiveCollection
{
void GetData(Parameter param, DataWriter writer);
void ShutDown();
}
还有初始化循环:
foreach(IDriverConfig config in DriverConfigs)
{
//Use MEF to load correct driver
var collector = this.DriverLoader(config.DriverId).GetCollection(new Logger, config);
// someTimer is an IObservable<Parameter> that triggers to tell when to collect data.
someTimer.Subscribe((param)=> collector.GetData(param, new DataWriter(param)));
}
问题是某些驱动程序可能需要比其配置更多的信息才能进行初始化.例如,某些驱动程序需要在初始化期间为它们提供一组参数.
我可以轻松地将界面扩展到现在看起来像:
interface IDriverLiveCollection
{
ILiveCollection GetCollection(ILog logger, IDriverConfig config, IEnumerable<Parameter> params);
}
这种方法的缺点是公共接口已经改变,现在我需要重新编译每个驱动程序,即使之前没有任何需要此参数列表才能运行.我打算有大量的驱动程序,我也无法控制谁编写驱动程序.
我想到了另一个解决方案.我可以创建接口,在我调用Get Collection之间和我订阅定时器之间的循环中,我可以检查ILiveCollection对象是否也扩展了其中一个接口:
interface InitWithParameters
{
void InitParams(IEnumerable<Parameter> params);
}
在我的循环中:
foreach(IDriverConfig config in DriverConfigs)
{
//Use MEF to load correct driver
var collector = this.DriverLoader(config.DriverId).GetCollection(new Logger, config);
// Check to see if this thing cares about params.
if(collector is InitWithParameters)
{
((InitWithparameters)collector).InitParams(ListOfParams);
}
// Continue with newly added interfaces.
// someTimer is an IObservable<Parameter> that triggers to tell when to collect data.
someTimer.Subscribe((param)=> collector.GetData(param, new DataWriter(param)));
}
这里的不同之处在于我不需要重新编译每个驱动程序以使其工作.旧驱动程序将不是InitWithParameters类型,并且不会以这种方式调用,而新驱动程序将能够利用新接口.如果旧驱动程序想要利用它,那么它可以简单地实现该接口并重新编译.底线:除非他们想要功能,否则我不需要重新编译驱动程序.
我已经认识到的缺点是:我显然需要重新编译哪个程序在这个循环中.当新驱动程序与旧版本的程序一起使用时,会出现版本控制问题,这可能会导致某些问题.最后,随着这些事情的发展,我必须在循环中保存一个包含程序中每种可能类型的巨大列表.
有一个更好的方法吗?
编辑附加信息:
我试图在IDriverLiveCollection上使用MEF,而不是在ILiveCollection上,因为IDriverLiveCollection允许我使用自定义初始化参数构造特定的ILiveCollection.
可以有2个相同类型的ILiveCollections(2个FooLiveCollections),每个ILiveCollections具有不同的ILog和IDriverConfig,并且可能具有IEnumerable.我希望能够在“初始化循环”期间指定这些,而不是在插件组成时.
最佳答案 如果您只是直接在ILiveCollection接口上使用[ImportMany],则可以通过
[ImportingConstructor]
属性处理整个基础结构.
这允许您的插件准确指定它们需要能够构造的内容,而不必在以后提供和构造类型.
实际上,您的主机应用程序只需要:
// This gets all plugins
[ImportMany]
IEnumerable<ILiveCollection> LiveCollections { get; set; }
然后每个插件都有它们导出它的类型,即:
[Export(typeof(ILiveCollection))]
public class FooLiveCollection : ILiveCollection
{
[ImportingConstructor]
public FooLiveCollection(ILog logger, IDriverConfig config)
{
// ...
或者,插件可以省略一个构造函数参数,或添加laters(不影响以前的插件),即:
[ImportingConstructor]
public BarLiveCollection(ILog logger, IDriverConfig config, IBarSpecificValue barParam)
{
// ...