[dependency-injection] DI 코드와 달리 IoC 컨테이너가 필요한 이유는 무엇입니까? [닫은]

내가 사용하고 의존성 삽입 (Dependency Injection) 생성자, 속성 또는 방법 중 하나를 주입, 잠시 동안 (DI)를. IoC ( Inversion of Control ) 컨테이너 를 사용할 필요가 없었습니다 . 그러나 읽을수록 IoC 컨테이너를 사용하라는 커뮤니티의 압력이 커집니다.

StructureMap , NInject , UnityFunq 와 같은 .NET 컨테이너를 가지고 놀았습니다 . 나는 여전히 IoC 컨테이너가 어떻게 코드를 향상 / 향상 시킬지 알지 못합니다.

또한 많은 동료들이 이해하지 못하는 코드를 볼 수 있기 때문에 직장에서 컨테이너를 사용하기를 두려워합니다. 그들 중 많은 사람들이 새로운 기술을 배우기를 꺼려 할 수 있습니다.

IoC 컨테이너를 사용해야한다고 확신하십시오. 직장 동료 개발자들과 이야기 할 때이 주장들을 사용할 것입니다.



답변

와, Joel이 이것을 선호한다고 믿을 수 없습니다.

var svc = new ShippingService(new ProductLocator(),
   new PricingService(), new InventoryService(),
   new TrackingRepository(new ConfigProvider()),
   new Logger(new EmailLogger(new ConfigProvider())));

이 위에 :

var svc = IoC.Resolve<IShippingService>();

많은 사람들은 의존성 체인이 중첩 될 수 있다는 것을 인식하지 못하고 수동으로 연결하는 것이 까다로워집니다. 공장에서도 코드 복제는 그만한 가치가 없습니다.

IoC 컨테이너는 복잡 할 수 있습니다. 그러나이 간단한 경우에는 매우 쉽다는 것을 보여주었습니다.


자, 이것을 더 정당화합시다. 스마트 UI에 바인딩하려는 일부 엔터티 또는 모델 개체가 있다고 가정 해 봅시다. 이 스마트 UI (Shindows Morms라고 함)는 INotifyPropertyChanged를 구현하여 변경 내용 추적 및 UI를 적절히 업데이트 할 수 있기를 원합니다.

“좋아, 그렇게 힘들지 않아”라고 쓰기 시작합니다.

이것으로 시작하십시오 :

public class Customer
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateTime CustomerSince { get; set; }
    public string Status { get; set; }
}

.. 그리고 함께 끝 :

public class UglyCustomer : INotifyPropertyChanged
{
    private string _firstName;
    public string FirstName
    {
        get { return _firstName; }
        set
        {
            string oldValue = _firstName;
            _firstName = value;
            if(oldValue != value)
                OnPropertyChanged("FirstName");
        }
    }

    private string _lastName;
    public string LastName
    {
        get { return _lastName; }
        set
        {
            string oldValue = _lastName;
            _lastName = value;
            if(oldValue != value)
                OnPropertyChanged("LastName");
        }
    }

    private DateTime _customerSince;
    public DateTime CustomerSince
    {
        get { return _customerSince; }
        set
        {
            DateTime oldValue = _customerSince;
            _customerSince = value;
            if(oldValue != value)
                OnPropertyChanged("CustomerSince");
        }
    }

    private string _status;
    public string Status
    {
        get { return _status; }
        set
        {
            string oldValue = _status;
            _status = value;
            if(oldValue != value)
                OnPropertyChanged("Status");
        }
    }

    protected virtual void OnPropertyChanged(string property)
    {
        var propertyChanged = PropertyChanged;

        if(propertyChanged != null)
            propertyChanged(this, new PropertyChangedEventArgs(property));
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

그것은 역겨운 배관 코드이며, 손으로 직접 코드를 작성 하면 클라이언트에서 훔치는 것 입니다. 더 좋고 똑똑한 작업 방법이 있습니다.

그 용어를 듣고 더 똑똑하지 않고 더 똑똑하게 일하십니까?

팀의 똑똑한 사람이 와서 다음과 같이 말했습니다. “더 쉬운 방법이 있습니다”

속성을 가상으로 만들면 (진정, 그다지 큰 문제는 아님) 해당 속성 동작을 자동으로 직조 할 수 있습니다 . (이를 AOP라고하지만 이름에 대해 걱정하지 말고 그것이 당신을 위해 무엇을 할 것인지에 집중하십시오)

사용중인 IoC 도구에 따라 다음과 같은 작업을 수행 할 수 있습니다.

var bindingFriendlyInstance = IoC.Resolve<Customer>(new NotifyPropertyChangedWrapper());

of! 해당 개체의 모든 가상 속성 설정 기에서 모든 수동 INotifyPropertyChanged BS가 자동으로 생성됩니다.

이거 마법이야? ! 이 코드가 작동한다는 사실을 신뢰할 수 있으면 mumbo-jumbo를 감싸는 해당 속성을 모두 건너 뛸 수 있습니다. 해결해야 할 비즈니스 문제가 있습니다.

AOP를 수행하기위한 IoC 도구의 다른 흥미로운 용도 :

  • 선언 및 중첩 데이터베이스 트랜잭션
  • 선언적이고 중첩 된 작업 단위
  • 벌채 반출
  • 사전 / 사후 조건 (계약에 의한 설계)

답변

당신과 함께 해요, 바딤 IoC 컨테이너는 단순하고 우아하며 유용한 개념을 취하며 200 페이지 분량의 매뉴얼로 이틀 동안 공부해야 할 내용으로 만듭니다.

저는 개인적으로 IoC 커뮤니티가 Martin Fowler 의 아름답고 우아한 기사를 가져 와서 200-300 페이지 매뉴얼로 복잡한 프레임 워크로 바꿨습니다.

나는 판단 적이 지 않지만 (HAHA!) IoC 컨테이너를 사용하는 사람들은 (A) 매우 똑똑하고 (B) 똑똑하지 않은 사람들에게는 공감이 없다고 생각합니다. 모든 것이 그들에게 완벽하게 이해되므로 많은 일반 프로그래머가 개념을 혼동한다는 것을 이해하는 데 어려움을 겪습니다. 지식저주입니다 . IoC 컨테이너를 이해하는 사람들은 컨테이너를 이해하지 못하는 사람들이 있다고 믿는 데 어려움을 겪습니다.

IoC 컨테이너를 사용하는 가장 큰 장점은 테스트 모드와 프로덕션 모드 사이를 전환 할 수있는 구성 스위치를 한 곳에 배치 할 수 있다는 것입니다. 예를 들어, 두 가지 버전의 데이터베이스 액세스 클래스가 있다고 가정합니다. 하나는 적극적으로 로깅하고 많은 검증을 수행 한 버전이고 개발 중에 사용한 많은 버전과 로깅 또는 유효성 검증이없는 다른 버전은 생산에 비약적으로 빠릅니다. 한곳에서 서로 전환 할 수있어 좋습니다. 반면에, 이것은 IoC 컨테이너의 복잡성없이 더 간단한 방법으로 쉽게 처리되는 상당히 사소한 문제입니다.

IoC 컨테이너를 사용하면 솔직히 코드를 읽기가 훨씬 어려워집니다. 코드가 수행하려는 작업을 파악하기 위해 살펴 봐야 할 곳의 수는 하나 이상 증가합니다. 그리고 하늘 어딘가에 천사가 외칩니다.


답변

아마도 아무도 DI 컨테이너 프레임 워크를 사용하도록 강요하지 않을 것입니다. 이미 DI를 사용하여 클래스를 분리하고 테스트 가능성을 향상시켜 많은 이점을 얻고 있습니다. 간단히 말해서, 단순성을 선호합니다. 일반적으로 좋은 것입니다.

시스템이 수동 DI가 번거로운 (즉, 유지 보수를 증가시키는) 복잡성 수준에 도달하면 DI 컨테이너 프레임 워크의 팀 학습 곡선과 비교하여 그 무게를 측정하십시오.

종속성 수명 관리에 대한 더 많은 제어가 필요한 경우 (즉, 싱글 톤 패턴을 구현해야한다고 생각되는 경우) DI 컨테이너를보십시오.

DI 컨테이너를 사용하는 경우 필요한 기능 만 사용하십시오. XML 구성 파일을 건너 뛰고 충분한 경우 코드에서 구성하십시오. 생성자 주입을 고수하십시오. Unity 또는 StructureMap의 기본 사항은 몇 페이지로 요약 될 수 있습니다.

Mark Seemann의 훌륭한 블로그 게시물이 있습니다 : DI 컨테이너 사용시기


답변

제 생각에는 IoC의 가장 큰 장점은 종속성 구성을 중앙 집중화하는 기능입니다.

현재 Dependency injection을 사용하는 경우 코드는 다음과 같습니다.

public class CustomerPresenter
{
  public CustomerPresenter() : this(new CustomerView(), new CustomerService())
  {}

  public CustomerPresenter(ICustomerView view, ICustomerService service)
  {
    // init view/service fields
  }
  // readonly view/service fields
}

더 혼란스러운 구성 파일 IMHO와 달리 정적 IoC 클래스를 사용한 경우 다음과 같은 내용이있을 수 있습니다.

public class CustomerPresenter
{
  public CustomerPresenter() : this(IoC.Resolve<ICustomerView>(), IoC.Resolve<ICustomerService>())
  {}

  public CustomerPresenter(ICustomerView view, ICustomerService service)
  {
    // init view/service fields
  }
  // readonly view/service fields
}

그런 다음 Static IoC 클래스는 다음과 같습니다. 여기서는 Unity를 사용하고 있습니다.

public static IoC
{
   private static readonly IUnityContainer _container;
   static IoC()
   {
     InitializeIoC();
   }

   static void InitializeIoC()
   {
      _container = new UnityContainer();
      _container.RegisterType<ICustomerView, CustomerView>();
      _container.RegisterType<ICustomerService, CustomerService>();
      // all other RegisterTypes and RegisterInstances can go here in one file.
      // one place to change dependencies is good.
   }
}


답변

IoC 컨테이너는 또한 중첩 된 클래스 종속성을로드하는 데 좋습니다. 예를 들어 Depedency Injection을 사용하여 다음 코드가있는 경우입니다.

public void GetPresenter()
{
    var presenter = new CustomerPresenter(new CustomerService(new CustomerRepository(new DB())));
}

class CustomerPresenter
{
    private readonly ICustomerService service;
    public CustomerPresenter(ICustomerService service)
    {
        this.service = service;
    }
}

class CustomerService
{
    private readonly IRespository<Customer> repository;
    public CustomerService(IRespository<Customer> repository)
    {
        this.repository = repository;
    }
}

class CustomerRepository : IRespository<Customer>
{
    private readonly DB db;
    public CustomerRepository(DB db)
    {
        this.db = db;
    }
}

class DB { }

이러한 모든 종속성이 IoC 컨테이너에로드 된 경우 CustomerService를 해결할 수 있으며 모든 하위 종속성이 자동으로 해결됩니다.

예를 들면 다음과 같습니다.

public static IoC
{
   private IUnityContainer _container;
   static IoC()
   {
       InitializeIoC();
   }

   static void InitializeIoC()
   {
      _container = new UnityContainer();
      _container.RegisterType<ICustomerService, CustomerService>();
      _container.RegisterType<IRepository<Customer>, CustomerRepository>();
   }

   static T Resolve<T>()
   {
      return _container.Resolve<T>();
   }
}

public void GetPresenter()
{
   var presenter = IoC.Resolve<CustomerPresenter>();
   // presenter is loaded and all of its nested child dependencies 
   // are automatically injected
   // -
   // Also, note that only the Interfaces need to be registered
   // the concrete types like DB and CustomerPresenter will automatically 
   // resolve.
}


답변

나는 선언적 프로그래밍의 팬입니다 (내가 대답하는 SQL 질문 수를보십시오). 그러나 내가 본 IoC 컨테이너는 자신의 이익을 위해 너무 비열한 것처럼 보입니다.

… 또는 아마도 IoC 컨테이너 개발자는 명확한 문서를 작성할 수 없습니다.

… 그렇지 않으면 둘 다 어느 정도 사실입니다.

나는 IoC 컨테이너 의 개념 이 나쁘지 않다고 생각합니다 . 그러나 구현은 다양한 응용 프로그램에 유용하면서도 간단하고 쉽게 이해할 수있을 정도로 강력해야합니다 (즉, 융통성).

아마 다른 하나의 것 중 하나 일 것입니다. 실제 응용 프로그램 (토이 또는 데모가 아님)은 복잡한 경우가 많으며 많은 경우에 적용되며 규칙에 대한 예외를 고려해야합니다. 명령 코드 또는 선언적 코드에서 복잡성을 캡슐화하십시오. 그러나 당신은 어딘가에 그것을 표현해야합니다.


답변

컨테이너를 사용하는 것은 주로 명령 및 스크립팅 스타일의 초기화 및 구성에서 선언적 스타일로 변경하는 것입니다. 몇 가지 다른 유익한 효과가있을 수 있습니다.

  • 감소 메인 프로그램 시작 루틴을.
  • 상당히 심도있는 배포 시간 재구성 기능을 가능하게합니다.
  • 의존성 주사 가능한 스타일을 새로운 작업에 대한 저항이 가장 적은 경로로 만듭니다.

물론 어려움이있을 수 있습니다.

  • 복잡한 시작 / 종료 / 라이프 사이클 관리가 필요한 코드는 컨테이너에 쉽게 적용되지 않을 수 있습니다.
  • 아마도 개인, 프로세스 및 팀 문화 문제를 탐색해야 할 것입니다. 그러나 그런 이유로 요청했습니다.
  • 일부 툴킷은 빠르게 헤비급이되어 많은 DI 컨테이너가 백래시로 시작한 일종의 깊은 의존성을 장려합니다.