6

多くの蹴りと叫び声を上げた後、依存関係が大きくなるにつれてSLがどれほどクリーンに見えるかもしれないにもかかわらず、私はDIを受け入れ始めています。

ただし、IMOには、DIに関して重要なショーストッパーがまだあります。

オブジェクトのインスタンス化を制御できない場合、DIは使用できません。ASP.NETの世界では、例にはHttpModule、HttpHandler、Pageなどがあります。

上記のシナリオでは、依存関係を解決するために静的なサービスの場所に頼ります。通常はHttpContext.Current、を介して、現在のスレッドから常にスコープを推測します。したがって、ここで静的SLを使用する場合は、他の場所でも使用してみませんか?

答えは次のように簡単ですか:歯を食いしばって、必要に応じてSLを使用します(上記のように)が、DIを支持してみてください。もしそうなら:静的SLを一度だけ使用すると、アプリケーション全体の一貫性が損なわれる可能性がありますか?本質的に他の場所でDIのハードワークを元に戻しますか?

4

4 に答える 4

16

例のように、密結合を避けられない場合があります。しかし、それはあなたがそれを受け入れる必要があるという意味でもありません。代わりに、乱雑さをカプセル化し、日常生活から切り離して隔離します。

たとえば、現在のフォーム認証ユーザーが必要な場合、にアクセスする以外に選択肢はありませんHttpContext.Current.Request.User.Identity.Name。ただし、どこに電話をかけるかは選択できます。

HttpContext.Current問題の解決策です。結果を使用する場所から直接呼び出す場合、同じ場所で問題と解決策を宣言しています。「現在のHTTPコンテキストからのものとして揺るぎなく宣言されている現在のユーザー名が必要です。」これは両方の定義を混乱させ、同じ問題に対する異なる解決策を可能にしません。

私たちが欠けているのは、私たちが解決しようとしている問題の明確な表現です。この例では、次のようになります。

現在のリクエストを行ったユーザーを特定します

HttpContext.Current、またはユーザー名を使用しているという事実は、主要な問題定義の一部ではありません。これは、現在のユーザーを必要とするコードを複雑にするためだけに役立つ実装の詳細です。

インターフェイスを介して、現在のユーザーを取得する意図を表すことができます。実装の詳細はありません。

public interface IUserContext
{
    User GetUser();
}

HttpContext.Current直接呼び出したクラスは、代わりにこのインターフェースを使用して、コンテナーによって注入され、DIの良さを維持できます。IUserContextまた、パブリックAPIからは見えない依存関係を持つよりも、クラスがコンストラクターでを受け入れることの方がはるかに意図を明らかにします。

実装は、静的呼び出しを単一の場所に埋め込んで、オブジェクトに害を及ぼすことができなくなります。

public class FormsUserContext : IUserContext
{
    private readonly IUserRepository _userRepository;

    public FormsUserContext(IUserRepository userRepository)
    {
        _userRepository = userRepository;
    }

    public User GetUser()
    {
        return _userRepository.GetByUserName(HttpContext.Current.Request.User.Identity.Name);
    }
}
于 2011-02-20T23:43:21.727 に答える
4

優れたコードベースは、最適なソリューションに対する時折の制限が小さな領域に含まれているコードベースでもあります。DIの可能性の欠如は、ASP.NETからMVCの対応物への移行を部分的に説明しています。HttpHandlerを使用すると、通常、「手」をダーティにしてコンテナからHttpHandlerをインスタンス化する唯一のクラスであるHttpHandlerファクトリがあります。

多くのDIコンテナには、すでにインスタンス化されているオブジェクトに対してセッターインジェクションを実行するための何らかのBuildUpメソッドがあります。サービスの場所がある場合は、それを純粋にインフラストラクチャの問題であるアプリケーションの一部に分離してみてください。その後、環境の制限に対する減衰フィールドとして機能します。

つまり、一部のSLは作業を無効にしませんが、SLビットがインフラストラクチャで十分に含まれていることを確認してください。

于 2011-02-20T22:08:26.523 に答える
2

@flqの回答に例を示します。

私は、Ninjectをかなり広範囲に使用するASP.NetMVCプロジェクトに取り組んでいます。DI(Extensionメソッド)を使用できないか、使用しない方がクリーンで、代わりにSLを使用する場所がいくつかありました(すべてのコントローラーの基本クラスであるコンストラクターインジェクションにより、コントローラーのすべてのコンストラクターが乱雑になります)。

したがって、妥協案として、私はわずかなハイブリッドを使用します。

public interface IServiceLocator
{
    T Create<T>();
}

public class NinjectServiceLocator : IServiceLocator
{
    private readonly IKernel kernel;

    public NinjectServiceLocator(IKernel kernel)
    {
        this.kernel = kernel;
    }

    public T Create<T>()
    {
        return kernel.Get<T>();
    }
}

public class ServiceLocator
{
    private static IServiceLocator current;

    public static IServiceLocator Current
    {
        get
        {
            return current;
        }

        set
        {
            current = value;
        }
    }

    public T Create<T>()
    {
        return current.Create<T>();
    }
}

それは最良の選択肢ではないかもしれませんし、厳密なSLパターンではないかもしれませんが、私にとってはうまくいきます。追加のインターフェースを使用すると、ロケーター自体をテスト用に交換できます。IKernelは構成されるNinjectのメインDIオブジェクトであるため、私が遭遇したほとんどのフレームワークは同様のコアを持っているため、独自のフレームワークに置き換えてください。

于 2011-02-20T23:56:29.827 に答える
2

このような場合の一般的なアプローチは、Service Locatorを使用するが、このアダプターを使用するアプリケーションレベルのクラスにクリーンなIoCを提供する汎用インフラストラクチャレベルのラッパー/アダプターを作成することです。

これは、 HttpModulesProvidersでIoCを有効にするために行いました。これは、フレームワークがインスタンス化の制御を許可しない2つのケースです。同様のアプローチは、他の同様のエンティティにも使用できます。

ここでのポイントは、そのようなラッパー/アダプターコードは再利用可能なインフラストラクチャの一部であり、アプリケーションレベルのコードではないということです。一般的に推奨されないのは、アプリケーションレベルのコードでサービスの場所を使用することです。

一部のコンテナが提供する「ビルドアップ」機能は、これらの制限に対する適切な回避策ではありません。BuildUpではインスタンス化の制御を取り戻すことができないため、プロキシなどの一部のコンテナ機能を使用できなくなります。

于 2011-02-21T01:03:24.283 に答える