c# – 插件Achitecture Extensibilty

我正在与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)
    {
       // ...
点赞