Programing

Ninject는 무엇이며 언제 사용합니까?

lottogame 2020. 12. 25. 08:58
반응형

Ninject는 무엇이며 언제 사용합니까?


프로젝트에서 친구 몇 명을 돕고 있는데 Ninject를 사용하는 수업이 있습니다. 저는 C #을 처음 접했고 그 클래스가 무엇을하는지 전혀 모릅니다. 그래서 Ninject를 이해해야합니다. 누구든지 Ninject가 무엇이며 언제 사용합니까 (가능한 경우 예제 포함)? 또는 좋은 링크를 가리킬 수 있다면.

나는이 질문을 시도했다 : Ninject 튜토리얼 / 문서? 하지만 저 같은 초보자에게는 도움이되지 않았습니다.


Ninject는 .NET을위한 의존성 주입기, 패턴 의존성 주입 (Inversion of Control 패턴의 형태)의 실질적인 실현입니다.

당신이 두 개의 클래스가 있다고 가정 DbRepositoryController:

class Controller {
   private DbRepository _repository;

   // ... some methods that uses _repository
}

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

이제 두 가지 문제가 있습니다.

  1. _repository사용 하려면 초기화해야 합니다. 다음과 같은 방법이 있습니다.

    1. 생성자에서 수동으로. 그러나 DbRepository 생성자가 변경되면 어떻게 될까요? Controller코드의 다른 부분이 변경 되었으므로 클래스 를 다시 작성해야합니다 . 하나만 있으면 어렵지 Controller않지만 의존성이있는 클래스가 두 개 있으면 Repository실제 문제가 있습니다.
    2. 서비스 로케이터 또는 공장 또는 smth를 사용할 수 있습니다. 이제 서비스 로케이터에 의존합니다. 글로벌 서비스 로케이터가 있으며 모든 코드에서이를 사용해야합니다. 코드의 한 부분에서 활성화 논리를 사용하고 코드의 다른 부분에서 다른 것을 사용해야 할 때 서비스 로케이터의 동작을 어떻게 변경합니까? 한 가지 방법은 생성자를 통해 서비스 로케이터를 전달하는 것입니다. 그러나 점점 더 많은 수업을 받으면 점점 더 통과해야 할 것입니다. 어쨌든 좋은 생각이지만 나쁜 생각입니다.

      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. 단위 테스트를 할 수 없습니다. 당신은 Controller에 대한 종속성이 DbRepository당신이 테스트를 사용 리포지토리 몇 가지 방법을 원하는 경우, 코드 데이터베이스로 이동 데이터를 요청합니다. 느리고 아주 느립니다. 코드가 DbRepository변경되면 단위 테스트 Controller가 떨어집니다. 이 경우 통합 테스트 만 '문제'를 표시해야합니다. 단위 테스트에서 필요한 것-클래스를 분리하고 하나의 테스트에서 하나의 클래스 만 테스트합니다 (이상적으로는 하나의 방법 만). 귀하의 경우 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)

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. Car에 대한 단위 테스트에서 다음을 수행 할 수 있습니다.

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

테스트 클래스를 자동으로 생성하는 전체 프레임 워크가 있으며이를 모의 프레임 워크라고합니다.

자세한 내용은:


다른 답변도 훌륭하지만 Ninject를 사용하여 종속성 주입 구현 기사 를 지적하고 싶습니다 .
이것은 Dependency Injection과 Ninject를 매우 우아한 예제로 설명하는 내가 읽은 최고의 기사 중 하나입니다.

다음은 기사의 스 니펫입니다.

아래 인터페이스는 (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) with totally different implementation using the same interface:

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);
 }
 }

we need to implement a change to our (UIHandler) class constructor to pass the dependency through it, by doing this, the code which uses the (UIHandler) can determine which concrete implementation of (ISMSService) to use:

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!");
 }
 }

Now, we have to create a separate class (NinjectBindings) which inherits from (NinjectModule). This class will be responsible to resolve dependencies at run time, then we’ll override the load event which is used to configure the binding in it. The nice thing about Ninject is that we do not need to change our code in (ISMSService), (SMSService), and (MockSMSService).

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

Now in UI form code, we’ll use the binding for Ninject which will determine which implementation to use:

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();
 }
 }

Now the code is using the Ninject Kernal to resolve all chain of dependencies, if we want to use the real service (SMSService) in Release mode (on production environment) instead of the mock one, we need to change on the Ninject binding class (NinjectBindings) only to use the right implementation or by using the #if DEBUG directive as below:

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

}
 }

Now our binding class (NinjectBindings) is living on the top of all our execution code and we can control the configuration easily in once place.


Also, see What is Inversion of Control? some very simple examples are mentioned to understand IoC.

ReferenceURL : https://stackoverflow.com/questions/17375234/what-is-ninject-and-when-do-you-use-it

반응형