2

私は依存性注入が初めてで、StackOverflow と他の場所の両方でそれについて読んでいます。実際には、正しく使用するのに問題があります。

問題を説明するために、DI の使用方法がわからない基本的な状況を次に示します。いくつかの異なるクラスで使用されるオブジェクトがあるとします。ただし、このオブジェクトを使用できるようにするには、起動時に持っていない特定のパラメーターが必要です。

DI を使用してこれを行うために考えられる方法は、このオブジェクトの空のインスタンス、必要なパラメーターで初期化するメソッド、および初期化されているかどうかのフラグを作成することです。

私には、これはハックのように感じます。なぜなら、オブジェクトは実際にはまだ存在してはならず、コンテナを渡し、責任のあるコードがそれを初期化するのを待っているだけだからです。これが意図された方法ですか、それとも私は要点を逃していますか?

4

2 に答える 2

2

これは、DI を使い始めるときに理解するのが非常に難しいことであり、簡単に説明することもできません。

メソッドを介して後で初期化される「空の」オブジェクトを作成することは、最適ではない解決策である可能性があるというあなたの考えは正しいです-オブジェクトはいつでもその作業を行うことができるはずです。Initialize()メソッドは、Mark Seemann が著書Dependency Injection in .NETで「時間結合」と呼んでいるものです。これは、オブジェクトを使用するコードをそのオブジェクトの内部動作に依存させ、カプセル化を破るアンチパターンです。

問題は、必要な情報がいつ利用可能になるか、「それを初期化する責任のあるコード」とは何か、情報をどこから取得するか、そしてオブジェクトを初期化するためにオブジェクトにアクセスする方法です。理想的には、この初期化コード自体がオブジェクトに注入され、オブジェクトのメソッド/プロパティがアクセスされるたびに、他の依存関係からの初期化を要求します。

また、IsInitializedフラグが false を返すとどうなりますか? それはまだ有効なプログラム状態ですか?

一般に、依存性が注入されたオブジェクト グラフ内のオブジェクトとして、作成時に自分のすべての「構成」データを知っているか、それを提供してくれる人 (依存性として注入された別のオブジェクトであること) を知っている必要があります。

オブジェクトが必要とするパラメーターの種類と、それらがどこから取得される必要があるかについて、もう少し詳しく説明していただけると助かります。

編集

あなたがコメントで説明していることは、この種の問題との最初の出会いです。私が当時投稿したSOのどこかに質問があります。

重要なことは、個々のクラスを構築することです (通常、例外はあるかもしれませんが、それが何であるかは経験の問題です)。クラスに必要なものがすべて揃っていると想定できるようにします。プログラムが実行されているとき、その仮定が失敗しないことを確認する他のクラスが必要です。

セッター注入は、私が通常、上記の一時的な結合を回避する必要がないように努めるものです。Mark Seemann によると、通常、setter インジェクションは、setter を介して上書きするだけの適切なデフォルトが既にある場合にのみ使用する必要があります。ただし、この場合、オブジェクトはその依存関係がなければ適切に機能できません。

これはこれを行うための最もエレガントな方法ではないかもしれません (私は通常、かなり閉じたコードのみの環境で、UI を気にせずに DI を適用する余裕があります)、それは機能します (一種の - コンパイルされますが、それでも擬似コード):

public class MainForm
{
    private readonly IDataManager _dataManager;
    private readonly IConnectionProvider _connectionProvider;
    private readonly IConnectionReceiver _connectionReceiver;

    public MainForm(IDataManager dataManager, IConnectionProvider connectionProvider, IConnectionReceiver connectionReceiver)
    {
        this._dataManager = dataManager;
        this._connectionProvider = connectionProvider;
        this._connectionReceiver = connectionReceiver;
    }

    public void btnConnect_Click()
    {
        IConnection connection = this._connectionProvider.GetConnection();

        if (null != connection)
        {
            this._connectionReceiver.SetConnection(connection);

            this.SetFormControlsEnabled(true);
        }
    }

    private void SetFormControlsEnabled(bool doEnable)
    {
    }
}

public interface IConnectionProvider
{
    IConnection GetConnection();
}

public interface IConnectionReceiver
{
    void SetConnection(IConnection connection);
}

public interface IConnection
{
    IConnectionWebService ConnectionWebService { get; }
}

public class ConnectionBridge : IConnection, IConnectionReceiver
{
    private IConnection _connection;

    #region IConnectionReceiver Members

    public void SetConnection(IConnection connection)
    {
        this._connection = connection;
    }

    #endregion IConnectionReceiver Members

    #region IConnection Members

    public IConnectionWebService ConnectionWebService
    {
        get { return this._connection.ConnectionWebService; }
    }

    #endregion
}

public interface IConnectionWebService {}

public interface IDataManager { }

public class DataManager : IDataManager
{
    public DataManager(IConnection connection)
    {
    }
}

それで、MainFormそれをすべて一緒に保持するものです。コントロールが無効になっている状態で開始します。これは、それらが機能する必要があり、IDataManager(慣例により) 接続が必要であることを認識しているためです。「接続」ボタンをクリックすると、フォームはそのIConnectionProvider依存関係に接続を要求します。その接続がどこから来るかは気にしません。接続プロバイダーは、別のフォームを表示して資格情報を要求したり、ファイルから資格情報を読み取ったりする場合があります。

次に、フォームは接続をIConnectionReceiverインスタンスに渡す必要があることを認識し、その後、すべてのコントロールを有効にできます。これは DI の原則によるものではありませんMainForm

一方、データ マネージャーには、最初から必要なものすべて (IConnectionインスタンス) があります。最初は本来の動作を実行できませんが、問題の発生を防止する別のコードがあります。

ConnectionBridge実際のインスタンスのデコレータであり、IConnection接続の消費から接続の取得を切り離すアダプタでもあります。これは、 Interface Segregation Principleを採用することによって行われます。

余談ですが、依存性注入は重要な手法ですが、「クリーン コード」と呼ばれるものを作成するために従うべきいくつかの原則の 1 つにすぎないことに注意してください。最もよく知られているのはSOLIDの原則 (DI はその 1 つ) ですが、他にもCommand-Query-Separation (CQS)"Don't repeat yourself" (DRY)Demeter の法則などがあります。その上で、単体テスト、正確にはテスト駆動開発 (TDD)を練習します。これらのことは本当に大きな違いをもたらします - しかし、あなたが自発的にDIを始めているなら、あなたはすでに良いスタートを切っています.

于 2013-03-14T18:48:22.427 に答える
0

私はGCATNMが言ったことに同意し、このようなオブジェクトがあると感じたときはいつでも、Factoryパターンのバリアントの1つ(Abstract Factory、Static Factoryなど)を使用して注入することを追加したいと思います.このオブジェクトの構成情報のソースを持つファクトリ。したがって、Marc Seemann も言っていますが、私は引用していません。ファクトリは依存性注入の優れた仲間であり、時々必要になるでしょう。

于 2013-03-14T23:04:37.727 に答える