私はプロジェクトで数人の友人を助けてきましたが、Ninjectを使用するクラスがあります。私はC#にかなり慣れていないので、そのクラスが何をしているかわからないので、Ninjectを理解する必要があります。誰でもNinjectが何であるか、そしていつそれを使用するかを説明できますか(可能な場合は例を挙げて)。または、素晴らしいリンクを指すことができるなら、それも素晴らしいでしょう。
私はこの質問を試しました: Ninject tutorials/documentations? しかし、それは本当に私のような初心者を助けませんでした。
Ninjectは、.NET用の依存性インジェクター、パターン依存性注入(制御パターンの反転の形式)の実用化です。
DbRepository
とController
の2つのクラスがあるとします:
_class Controller {
private DbRepository _repository;
// ... some methods that uses _repository
}
class DbRepository {
// ... some bussiness logic here ...
}
_
そのため、2つの問題があります。
使用するには__repository
_を初期化する必要があります。次のような方法があります。
Controller
クラスを書き換える必要があります。 Controller
が1つしかない場合は難しくありませんが、Repository
に依存するクラスがいくつかある場合、実際の問題が発生します。サービスロケーター、工場、またはsmthを使用できます。これで、サービスロケーターに依存するようになりました。グローバルサービスロケーターがあり、すべてのコードで使用する必要があります。コードのある部分で1つのアクティベーションロジックを使用し、他の部分をコードの他の部分で使用する必要がある場合、サービスロケーターの動作をどのように変更しますか?唯一の方法があります-サービスロケーターをコンストラクターに渡す。しかし、ますます多くのクラスを使用して、それをますます渡す必要があります。とにかく、それはいい考えですが、悪い考えです。
_class Controller {
private DbRepository _repository;
public Controller() {
_repository = GlobalServiceLocator.Get<DbRepository>()
}
// ... some methods that uses _repository
}
_
依存性注入を使用できます。コードを見てください:
_class Controller {
private IRepository _repository;
public Controller(IRepository repository) {
_repository = repository;
}
}
_
コントローラーが必要な場合は、ninjectDevKernel.Get<Controller>();
またはninjectTestKernel.Get<Controller>();
と書きます。依存関係リゾルバ間を必要なだけ切り替えることができます。見る?簡単です。たくさん書く必要はありません。
単体テストを行うことはできません。 Controller
はDbRepository
に依存しており、リポジトリを使用するメソッドをテストする場合、コードはデータベースに移動してデータを要求します。それはとても遅いです。 DbRepository
のコードが変更されると、Controller
の単体テストが失敗します。この場合、統合テストのみが「問題」を言う必要があります。単体テストに必要なもの-クラスを分離し、1つのテストで1つのクラスのみをテストする(理想的には1つのメソッドのみ)。 DbRepository
コードが失敗すると、Controller
コードが失敗したと思うでしょう-そしてそれは悪いです(たとえDbRepository
とController
のテストがあっても-それらは両方とも失敗し、間違った場所から開始できます)。エラーが実際にどこにあるかを判断するのに多くの時間がかかります。一部のクラスは正常であり、他のクラスは失敗したことを知る必要があります。
すべてのクラスでDbRepository
を他のものに置き換える場合、多くの作業を行う必要があります。
DbRepository
のライフタイムを簡単に制御することはできません。このクラスのオブジェクトは、Controller
の初期化時に作成され、Controller
が削除されると削除されます。 Controller
クラスの異なるインスタンス間での共有はなく、他のクラス間での共有はありません。 Ninjectを使用すると、次のように簡単に記述できます。
_kernel.Bind<IRepository>().To<DbRepository>().InSingletonScope();
_
依存性注入の特別な機能-アジャイル開発!コントローラーがインターフェースIRepository
でリポジトリーを使用することを説明します。 DbRepository
を書く必要はありません。簡単なMemoryRepository
クラスを書いてController
を開発し、他の人がDbRepository
を開発できます。 DbRepository
が終了したら、依存関係リゾルバでデフォルトのIRepository
がDbRepository
になったことを再バインドします。多くのコントローラーを作成しましたか?それらはすべてDbRepository
を使用するようになります。カッコいい。
続きを読む:
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>();
これは、ユニットテストがより簡単なシステムを構築できるため、有益です。 Driver
がCar
のすべてのデータベースアクセスをカプセル化するとします。車の単体テストでは、次のことができます。
IDriver driver = new TestDriver(); // a fake driver that does not go to the db
var car = new Car(driver);
テストクラスを自動的に作成するフレームワーク全体があり、それらはモックフレームワークと呼ばれます。
詳細については:
他の回答も素晴らしいですが、この 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を理解するために、いくつかの非常に簡単な例を紹介します。