web-dev-qa-db-ja.com

Ninjectとは何ですか?いつ使用しますか?

私はプロジェクトで数人の友人を助けてきましたが、Ninjectを使用するクラスがあります。私はC#にかなり慣れていないので、そのクラスが何をしているかわからないので、Ninjectを理解する必要があります。誰でもNinjectが何であるか、そしていつそれを使用するかを説明できますか(可能な場合は例を挙げて)。または、素晴らしいリンクを指すことができるなら、それも素晴らしいでしょう。

私はこの質問を試しました: Ninject tutorials/documentations? しかし、それは本当に私のような初心者を助けませんでした。

56
bachkoi32

Ninjectは、.NET用の依存性インジェクター、パターン依存性注入(制御パターンの反転の形式)の実用化です。

DbRepositoryControllerの2つのクラスがあるとします:

_class Controller {
   private DbRepository _repository;

   // ... some methods that uses _repository
}

class DbRepository {
   // ... some bussiness logic here ...
}
_

そのため、2つの問題があります。

  1. 使用するには__repository_を初期化する必要があります。次のような方法があります。

    1. コンストラクターで手動で。しかし、DbRepositoryのコンストラクターが変更された場合はどうなりますか?コードの他の部分が変更されたため、Controllerクラスを書き換える必要があります。 Controllerが1つしかない場合は難しくありませんが、Repositoryに依存するクラスがいくつかある場合、実際の問題が発生します。
    2. サービスロケーター、工場、またはsmthを使用できます。これで、サービスロケーターに依存するようになりました。グローバルサービスロケーターがあり、すべてのコードで使用する必要があります。コードのある部分で1つのアクティベーションロジックを使用し、他の部分をコードの他の部分で使用する必要がある場合、サービスロケーターの動作をどのように変更しますか?唯一の方法があります-サービスロケーターをコンストラクターに渡す。しかし、ますます多くのクラスを使用して、それをますます渡す必要があります。とにかく、それはいい考えですが、悪い考えです。

      _class Controller {
         private DbRepository _repository;
      
         public Controller() {
           _repository = GlobalServiceLocator.Get<DbRepository>()
         }
      
         // ... some methods that uses _repository
      }
      _
    3. 依存性注入を使用できます。コードを見てください:

      _class Controller {
         private IRepository _repository;
      
         public Controller(IRepository repository) {
            _repository = repository;
         }
      }
      _

    コントローラーが必要な場合は、ninjectDevKernel.Get<Controller>();またはninjectTestKernel.Get<Controller>();と書きます。依存関係リゾルバ間を必要なだけ切り替えることができます。見る?簡単です。たくさん書く必要はありません。

  2. 単体テストを行うことはできません。 ControllerDbRepositoryに依存しており、リポジトリを使用するメソッドをテストする場合、コードはデータベースに移動してデータを要求します。それはとても遅いです。 DbRepositoryのコードが変更されると、Controllerの単体テストが失敗します。この場合、統合テストのみが「問題」を言う必要があります。単体テストに必要なもの-クラスを分離し、1つのテストで1つのクラスのみをテストする(理想的には1つのメソッドのみ)。 DbRepositoryコードが失敗すると、Controllerコードが失敗したと思うでしょう-そしてそれは悪いです(たとえDbRepositoryControllerのテストがあっても-それらは両方とも失敗し、間違った場所から開始できます)。エラーが実際にどこにあるかを判断するのに多くの時間がかかります。一部のクラスは正常であり、他のクラスは失敗したことを知る必要があります。

  3. すべてのクラスでDbRepositoryを他のものに置き換える場合、多くの作業を行う必要があります。

  4. DbRepositoryのライフタイムを簡単に制御することはできません。このクラスのオブジェクトは、Controllerの初期化時に作成され、Controllerが削除されると削除されます。 Controllerクラスの異なるインスタンス間での共有はなく、他のクラス間での共有はありません。 Ninjectを使用すると、次のように簡単に記述できます。

    _kernel.Bind<IRepository>().To<DbRepository>().InSingletonScope();
    _

依存性注入の特別な機能-アジャイル開発!コントローラーがインターフェースIRepositoryでリポジトリーを使用することを説明します。 DbRepositoryを書く必要はありません。簡単なMemoryRepositoryクラスを書いてControllerを開発し、他の人がDbRepositoryを開発できます。 DbRepositoryが終了したら、依存関係リゾルバでデフォルトのIRepositoryDbRepositoryになったことを再バインドします。多くのコントローラーを作成しましたか?それらはすべてDbRepositoryを使用するようになります。カッコいい。

続きを読む:

  1. 制御の反転(wiki)
  2. 依存性注入(wiki)
  3. コントロールコンテナの反転と依存性注入パターン(Martin Fowler)
46
Viktor Lova

Ninjectは、Inversion of Controlコンテナーです。

それは何をするためのものか?

Carクラスに依存するDriverクラスがあるとします。

public class Car 
{
   public Car(IDriver driver)
   {
      ///
   }
}

Carクラスを使用するには、次のようにビルドします。

IDriver driver = new Driver();
var car = new Car(driver);

IoCコンテナは、クラスの構築方法に関する知識を集中化します。いくつかのことを知っている中央リポジトリです。たとえば、自動車の製造に使用する必要がある具体的なクラスはDriverであり、他のIDriverではないことを知っています。

たとえば、MVCアプリケーションを開発している場合、コントローラーの構築方法をNinjectに伝えることができます。これは、どの具象クラスが特定のインターフェースを満たすかを登録することにより行います。実行時に、Ninjectは、必要なコントローラーを構築するために必要なクラスと、すべての舞台裏を把握します。

// Syntax for binding
Bind<IDriver>().To<Driver>();

これは、ユニットテストがより簡単なシステムを構築できるため、有益です。 DriverCarのすべてのデータベースアクセスをカプセル化するとします。車の単体テストでは、次のことができます。

IDriver driver = new TestDriver(); // a fake driver that does not go to the db
var car = new Car(driver);

テストクラスを自動的に作成するフレームワーク全体があり、それらはモックフレームワークと呼ばれます。

詳細については:

41
Sklivvz

他の回答も素晴らしいですが、この Ninject 記事を使用した依存性注入の実装も指摘したいと思います。
これは非常にエレガントな例でDependency InjectionとNinjectを説明する、これまで読んだ中で最高の記事の1つです。

記事の抜粋です:

以下のインターフェイスは(SMSService)および(MockSMSService)によって実装され、基本的に新しいインターフェイス(ISMSService)は両方のサービスの同じ動作を以下のコードとして公開します。

public interface ISMSService
 {
 void SendSMS(string phoneNumber, string body);
 }

(ISMSService)インターフェースを実装する(SMSService)実装:

public class SMSService : ISMSService
 {
 public void SendSMS(string mobileNumber, string body)
 {
 SendSMSUsingGateway(mobileNumber, body);
 }




private void SendSMSUsingGateway(string mobileNumber, string body)
 {
 /*implementation for sending SMS using gateway*/
Console.WriteLine("Sending SMS using gateway to mobile: 
    {0}. SMS body: {1}", mobileNumber, body);
 }
 }

(MockSMSService)同じインターフェースを使用した完全に異なる実装:

public class MockSMSService :ISMSService
 {
 public void SendSMS(string phoneNumber, string body)
 {
 SaveSMSToFile(phoneNumber,body);
 }

private void SaveSMSToFile(string mobileNumber, string body)
 {
 /*implementation for saving SMS to a file*/
Console.WriteLine("Mocking SMS using file to mobile: 
    {0}. SMS body: {1}", mobileNumber, body);
 }
 }

(UIHandler)クラスコンストラクターに変更を実装して依存関係を渡す必要があります。これにより、(UIHandler)を使用するコードは(ISMSService)の具体的な実装を決定できます。

public class UIHandler
 {
 private readonly ISMSService _SMSService;

public UIHandler(ISMSService SMSService)
 {
 _SMSService = SMSService;
 }
 public void SendConfirmationMsg(string mobileNumber) {

 _SMSService.SendSMS(mobileNumber, "Your order has been shipped successfully!");
 }
 }

次に、(NinjectModule)を継承する別のクラス(NinjectBindings)を作成する必要があります。このクラスは、実行時に依存関係を解決する役割を果たします。その後、バインディングを構成するために使用されるロードイベントをオーバーライドします。 Ninjectの良いところは、(ISMSService)、(SMSService)、および(MockSMSService)のコードを変更する必要がないことです。

public class NinjectBindings : Ninject.Modules.NinjectModule
 {
 public override void Load()
 {
 Bind<ISMSService>().To<MockSMSService>();
 }
 }

UIフォームコードでは、使用する実装を決定するNinjectのバインディングを使用します。

class Program
 {
 static void Main(string[] args)
 {
 IKernel _Kernal = new StandardKernel();
 _Kernal.Load(Assembly.GetExecutingAssembly());
 ISMSService _SMSService = _Kernal.Get<ISMSService>();

UIHandler _UIHandler = new UIHandler(_SMSService);
 _UIHandler.SendConfirmationMsg("96279544480");

Console.ReadLine();
 }
 }

現在、コードはNinject Kernalを使用してすべての依存関係のチェーンを解決しています。モックサービスではなく、リリースモード(実稼働環境)で実際のサービス(SMSService)を使用する場合は、Ninjectバインディングクラス( NinjectBindings)適切な実装のみを使用するか、以下のように#if DEBUGディレクティブを使用します:

public class NinjectBindings : Ninject.Modules.NinjectModule
 {
 public override void Load()
 {
#if DEBUG
 Bind<ISMSService>().To<MockSMSService>();
#else
 Bind<ISMSService>().To<SMSService>();
#endif

}
 }

これで、バインディングクラス(NinjectBindings)がすべての実行コードの最上位にあり、一度に簡単に構成を制御できます。


また、 Inversion of Controlとは何ですか? IoCを理解するために、いくつかの非常に簡単な例を紹介します。

6
JerryGoyal