4

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 を使用すると、カスタム初期化パラメーターを使用して特定の ILiveCollection を構築できるため、ILiveCollection ではなく IDriverLiveCollection で MEF を使用しようとしています。

同じタイプの 2 つの ILiveCollections (2 つの FooLiveCollections) をそれぞれ異なる ILog と IDriverConfig で持つことができ、場合によっては IEnumerable を持つことができます。プラグインの構成時ではなく、「初期化ループ」中にこれらを指定できるようにしたいと思います。

4

1 に答える 1

2

[ImportMany]インターフェイスで直接使用する場合は、属性ILiveCollectionを介してこのインフラストラクチャ全体を処理できます。[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)
    {
       // ...

または、代わりに、プラグインは1つのコンストラクター引数を省略したり、後で(以前のプラグインに影響を与えずに)追加したりできます。

    [ImportingConstructor]
    public BarLiveCollection(ILog logger, IDriverConfig config, IBarSpecificValue barParam)
    {
       // ...
于 2012-05-31T17:40:10.823 に答える