2

私はアプリケーションをリファクタリングして依存性注入 (コンストラクター注入による) を含めるようにしていますが、トリッキーなコーナーケースに遭遇しました:

現在、インスタンス化されたときに(抽象基本クラス) インスタンスImageViewerのアセンブリを検索し、リフレクションを使用してそれらをインスタンス化するオブジェクトがあります。ImageViewerPluginこれは、ImageViewer次のようなメソッド (すべての具体的なプラグイン タイプのループで呼び出される) を使用するコンストラクターで行われます。

private ImageViewerPlugin LoadPlugin(Type concretePluginType)
{
  var pluginConstructor = concretePluginType.GetConstructor(
    BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public,
    null,
    new[] { typeof(ImageViewer) },
    null);

  return (ImageViewerPlugin) pluginConstructor.Invoke(
    new object[] { constructorParameter });
}

ImageViewerPlugin クラスは、おおよそ次のようになります。

internal ImageViewerPlugin
{
  protected ImageViewer _viewer;
  protected ImageViewerPlugin(ImageViewer viewer)
  {
    _viewer = viewer;
  }
}

具体的な実装は、おおよそ次のようになります。

internal AnImageViewerPlugin
{
  public AnImageViewerPlugin(ImageViewer viewer) : base(viewer)
  {
  }
}

ImageViewerインスタンスには、独自のImageViewerPluginインスタンスのコレクションがあります。

アプリケーションが DI コンテナーとコンストラクター インジェクションを使用するようにリファクタリングされたので、これらのプラグインには、DI コンテナーによって解決する必要がある依存関係 (以前はグローバル静的クラスの使用によって隠されていました) があることがわかりましたが、私はService Locator (アンチパターン) を使用せずにそれを行う方法がわからない。

最も賢明な解決策は、DI を使用してこれらのプラグイン インスタンスを作成することです。これにより、追加のコンストラクター パラメーターを追加して、依存関係をコンストラクター インジェクションを介して注入することができます。viewerしかし、それを行う場合、残りのパラメーター値を注入しながら特定のパラメーター値を渡すにはどうすればよいでしょうか?

ImageViewerPluginFactoryこれを達成するのに役立つと思いましたが、各プラグインが異なるコンストラクター署名を持つ可能性が高いため、そのようなファクトリを実装する方法がわかりません。

このシナリオをどのように解決できますか? それとも、これに完全に間違った方法でアプローチしていますか?

4

1 に答える 1

2

つまり、インスタンスのコレクションに依存するがあり、それぞれがインスタンスのコレクションに依存するnImageViewerに依存し、それぞれがインスタンスのコレクションに依存するnに依存し、それぞれが依存する...写真 :-)ImageViewerPluginImageViewerImageViewerPluginImageViewerImageViewerPlugin

これは循環参照です。DIコンテナ全体を別にして、これを手動で行う場合、この階層をどのように作成しますか?コンストラクターインジェクションではできません。次の例からわかります。

var plugin = new ImageViewerPlugin( [what goes here?] );
var viewer = new ImageViewer(plugin);

この依存関係のサイクルをなんとかして中断する必要があります。一般的なアドバイスは、この場合はプロパティの注入にフォールバックすることです。

var plugin = new ImageViewerPlugin();
var viewer = new ImageViewer(plugin);

// Property injection
plugin.Viewer = viewer;

ただし、循環参照は設計の問題を示していることが多いため、アプリケーションの設計をよく検討する必要があります。たとえば、プラグインがビューアから必要とする動作と、ビューアがプラグインから必要とする動作をよく見てください。次のように、これを別のクラスに抽出できる場合があります。

var imageServices = new ImageServices();
var plugin = new ImageViewerPlugin(imageServices);
var viewer = new ImageViewer(imageServices, plugin);

これは問題を完全に解決しますが、これが実行可能かどうかは状況によって異なります。

最後のソリューションでは、SimpleInjectorへの登録はかなり簡単です。プロパティインジェクションを使用して依存関係を解除する場合は、RegisterInitializer(Action)メソッドを使用できます。これにより、依存関係のサイクルを断ち切ることができます。例えば:

container.RegisterInitializer<ImageViewer>(viewer =>
{
    foreach (var plugin in viewer.Plugins)
    {
        plugin.Viewer = viewer;
    }
});
于 2013-03-15T00:38:15.550 に答える