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


598

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

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

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

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


6
좋은 질문. 나는 IOC의 아이디어에 매우 익숙하기 때문에 의견에 이것을 대답하고 있습니다. 플러그 앤 플레이 구성 요소와 느슨한 결합이라는 아이디어처럼 보입니다. 현재 구성 요소 대신 다른 구성 요소를 사용해야하는지 여부는 필요 기반입니다. IOC의 사용은 IMO가 발생할 경우 그러한 변경에 대한 코드를 준비하는 것입니다.
shahkalpesh

3
@ shahkalpesh 그러나 간단한 DI로 느슨한 결합을 얻을 수 있습니다. 그러나 구성 파일을 사용하는 경우의 요점을 알 수 있습니다. 그러나 구성 파일을 사용하고 있는지 확실하지 않습니다. 구성 파일은 매우 장황하며 리팩토링 및 여러 파일 간 전환에 어려움이 있습니다.
Vadim

110
IoC를 주로 사용해야하는 이유를 찾고있는 것 같아서 주석을 추가하는 것만; 하지만 문제에 대해 다른 말을해야합니다. 팀의 나쁜 사람이나 배우고 싶지 않은 두려움에 코드를 작성할 수 없습니다. 당신은 전문가가 될뿐만 아니라 미래에 성장해야 할 책임이 있으며 팀이 당신을 붙잡아서는 안됩니다.
케빈 셰필드

4
@Kevin, 실제로 우리는 IoC를 사용하고 있습니다. 모든 사람들에게 IoC에 대해 가르치는 것은 그리 어렵지 않았습니다.
Vadim

21
중재자가 왜 대대적으로 호의적이고 유용한 질문을 마감했는지 모르겠습니다. 아마도이 경우, 질문을 이해하지 못합니까, 아니면 사람들이 스택 교환을 위해 무엇을 사용하는지 이해하지 못합니까? "stackexchange의 Q & A 형식에 적합하지 않은 경우"라면 정책이 잘못되었다고 생각할 수 있습니다. 수백 개의 투표와 유용한 투표로 수백 가지 유용한 질문이 모두 해당되는 것은 아닙니다.
NickG

답변:


441

와, 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 도구의 다른 흥미로운 용도 :

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

28
죄송합니다. 모든 속성을 가상으로 만드는 것을 잊어 버렸습니다. 작동하지 않습니다. 클라이언트에서이 도둑질을 디버깅하는 데 필요한 시간입니까? (DynamicProxy를 사용하지 않는 경우에 사과드립니다. : P)
Thom

17
INotifyPropertyChanged 랩퍼를 사용하여 많은 명확성을 희생합니다. 배관 공사를 시작하는 데 몇 분 이상이 걸리면 사업이 잘못되었습니다. 코드의 장황함에 따라 더 적습니다.
2009 년

39
@ overstood-나는 완전히 동의하지 않습니다. 첫째,이 예에서 명확성은 어디에 중요합니까? 가장 가까운 소비자. 소비자가 명시 적으로 INotifyPropertyChanged를 요구하고 있습니다. 마이크로 또는 매크로 코드 생성으로 이러한 유형의 코드를 생성 할 있다고 해서 코드를 작성하는 것이 좋습니다. INotifyPropertyChanged gunk은 단순히 부패하고 평범한 배관이며 실제로 해당 클래스의 가독성을 떨어 뜨립니다.
Ben Scheirman

31
의존성 주입 패턴과 서비스 로케이터 패턴을 혼동하기 때문에 표시되지 않았습니다. 전자는 후자 대신 사용됩니다. 예를 들어 객체 (또는 실제로 전체 시스템)는 IShippingService의 인스턴스를 얻기 위해 Resolve (...)를 호출해서는 안됩니다. IShippingService가 필요한 개체는 생성시 사용해야하는 인스턴스에 대한 참조를 받아야하며 일부 상위 계층은 두 개체를 함께 연결해야합니다.
Nat

30
이것이 무엇이든 (IoC, DynamicProxy, AOP 또는 black magic ) 누군가 이것을 달성하기위한 자세한 내용을 제공하는 구체적인 기사 / 특정 문서에 링크 할 수 있습니까?
fostandy

138

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

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

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

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

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


81
나는 당신이 당신의 사실을 약간의 Joel과 혼합했다고 생각합니다. IoC와 컨테이너가 가장 먼저 나타 났으며, 그 반대가 아니라 Martin의 논문이되었습니다.
Glenn Block

55
대부분의 용기를 사용하면 매우 빨리 갈 수 있습니다. autofac, 구조 맵, 단일성, ninject 등을 사용하면 컨테이너를 약 5 분 안에 작동시킬 수 있어야합니다. 그렇습니다. 고급 기능이 있지만 그 기능을 사용할 필요는 없습니다.
Glenn Block

58
Joel 저는 당신과 같은 생각을했습니다. 그런 다음 말 그대로 5 분 동안 가장 기본적인 설정을 실행하는 방법을 알아 내고 80/20 규칙에 놀랐습니다. 특히 간단한 경우 코드를 훨씬 깨끗하게 만듭니다.
Justin Bozonier

55
나는 2 년 미만 동안 프로그래밍을 해왔고 Ninject 위키를 읽고 그것을 얻었습니다. 그 이후로 많은 곳에서 DI를 사용해 왔습니다. 그거 봤어? 2 년도 안되어 위키에서 몇 페이지를 읽습니다. 나는 마법사 또는 아무것도 아니다. 나는 나 자신이 천재라고 생각하지 않지만 이해하기 쉽다. OO를 이해하지 못하는 사람을 실제로 이해하는 사람은 상상할 수 없습니다. 또한 어떤 200-300 페이지 매뉴얼을 참조하고 있습니까? 나는 본 적이 없다. 내가 사용한 모든 IoC 컨테이너는 꽤 짧고 요점입니다.
JR 가르시아

35
+1 잘 쓰고 생각했습니다. 많은 사람들이 알지 못하는 것 중 하나는 프레임 워크 나 이와 같은 것을 "매직 적으로"처리하기 위해 추가하면 시스템에 복잡성과 제한이 자동으로 추가된다는 것입니다. 때로는 가치가 있지만 제한 사항으로 인해 발생하는 문제를 해결하기 위해 코드에 엔트로피가 공개되는 경우가 종종 있습니다. 사전에 시간을 절약 할 수 있지만 사람들은 모호한 문제를 해결하기 위해 100 배의 비용을 지불해야한다는 사실을 알아야합니다.
kemiller2002

37

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

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

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

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

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


매우 좋은 반응. 내가 의견을 달리 할 수있는 유일한 것은 수동 주입이 덜 복잡한 것으로 간주 될 수 있는지 아닌지 여부입니다.
wekempf

+1. 가장 좋은 답변 중 하나입니다. 블로그 기사 링크에도 감사드립니다.
stakx-더 이상

1
균형 잡힌 관점의 경우 +1 IoC 컨테이너가 항상 좋은 것은 아니며 항상 나쁜 것은 아닙니다. 필요가 없으면 사용하지 마십시오. 필요한 경우 도구가 있다는 것을 알기 만하면됩니다.
Phil

32

제 생각에는 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.
   }
}

11
벤, 당신이 말하는 것은 실제로 의존성 주입이 아니라 서비스 위치입니다. 차이점이 있습니다. 나는 항상 후자를 선호합니다. stevenharman.net/blog/archive/2009/09/25/…
stevenharman

23
기본 생성자에서 IoC.Resolve <T>를 수행하는 대신 IOC 컨테이너의 기능을 활용하여 객체를 생성해야합니다. 빈 생성자를 제거하고 IOC 컨테이너가 객체를 빌드하도록합니다. var presenter = IoC.Resolve <고객 프리젠 테이션> (); 짜잔! 모든 의존성이 이미 연결되어 있습니다.
Ben Scheirman

12
이런 종류의 구성의 중앙 집중화의 단점은 지식의 맥락화가 아닙니다. Joel은이 문제가 "코드를 읽기가 더 어렵다"고 말합니다.
Scott Bellware

6
@sbellware, 어떤 지식? 원시 의존성으로 간주되는 인터페이스는 계약 역할을하며 목적과 행동을 모두 전달하기에 충분해야합니다. 계약을 구현할 때 얻은 지식을 언급하고 있다고 가정하고 적절한 경우 적절한 구성을 추적해야합니까? 나는 그래프의 노드와 가장자리를 탐색하는 데 뛰어 나기 때문에 컴퓨터 (VS, IntelliJ 등의 R #)에 의존합니다. 나는 현재 집중하고있는 작업장의 맥락에서 내 두뇌를 쌓아 둔다.
stevenharman

7
Steve, 나는 .NET 텍스트를 알고 몇 년 동안 그 길을 걸었습니다. 또한 다른 모드와 형식에 친숙하며 친숙한 점이 있음을 눈에 띄게 이해합니다. 나는 그 절충점을 언제 어떻게 만들지 알고 있지만, 직장 생활의 다양성으로 인해 Joel의 관점을 확실하게 이해할 수 있습니다. 대부분의 .NET 모노 컬처보다 오랫동안 사용했던 도구와 요령에 대해 강의하는 대신 모르는 것을 말해주십시오. 나는 alt.net 정체와 정통보다는 다양한 비판적 사고에 대한 내 두뇌의 스택을 보유합니다.
Scott Bellware

32

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.
}

그러나 IoC 클래스는 서비스 로케이터 안티 패턴을 사용하여 호출됩니다. 정적 T Resolve <T> ()를 호출하는 대신 생성자 DI를 사용하여 should_container를 전달합니까?
Michael Freidgeim

@bendewey이 예는 인수가 인수를 새로운 CustomerService 및 새 CustomerRepository의 생성자로 자동 전달 함을 의미합니다. 이것이 어떻게 작동합니까? 다른 추가 매개 변수가있는 경우 어떻게합니까?
Brain2000

28

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

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

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

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

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


5
나는 불가피한 복잡성의 존재에 대한 당신의 관찰을 좋아합니다. 컨테이너 기반 배선과 수동 종속성 배선의 차이점은 컨테이너를 사용하면 각 구성 요소에 개별적으로 집중할 수있어 상당히 선형적인 방식으로 복잡성을 증가시킬 수 있다는 것입니다. 한 구성 요소를 구성하는 작업은 다른 구성 요소를 구성하는 것과 분리하기 어렵 기 때문에 수동 종속성 배선이있는 시스템은 발전하기가 더 어렵습니다.
Nicholas Blumhardt

AspectJ를 좋아합니다. Java는 아니지만 XML도 아닙니다. 그것은 일종의 IS Java인데, 그것은 확장되어 Java와 같은 느낌과 모양을 갖습니다. 차라리 POJO 및 비즈니스 로직에서 내 측면을 유지하고 싶습니다. IoC도 거의 유지하고 싶습니다. 도구를 배선 할 때 XML이 너무 많습니다 (IMHO). 구성 파일이 있어야하는 경우 BeanShell 스크립트로 사용하십시오. / Java의 경우 .Net 작업을 여기에 적용하십시오->.
Chris K

2
나는 당신의 대답을 확실히 이해하지만 문제의 큰 부분은 많은 IoC 프레임 워크 (그리고 실제로 다른 것들에 대한 다른 프레임 워크도 많이 설명되어 있음)는 이미 필수 개념 및 용어. 나는 다른 프로그래머의 코드를 상속받은 많은 것에 대해 배우고 있습니다. '객체 그래프'가 상당히 작고 코드의 실제 테스트 범위가 없을 때 코드가 IoC를 사용하는 이유를 이해하기 위해 고심하고 있습니다. SQL 프로그래머는 ORM과 함께 IoC를 사용하는 것이 좋습니다.
Kenny Evitt

1
@KennyEvitt, 좋은 지적은, 간단한 앱을 가지고 있고 테스트 범위가 좋지 않은 경우 IoC 프레임 워크를 사용하는 것이 너무 이른 것 같습니다.
Bill Karwin

27

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

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

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

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

23

Martin Fowler가 설명한 다양한 패턴을 사용하여 이미 자신의 IoC 컨테이너를 구축 하고 다른 사람의 구현이 왜 나보다 나은지 묻는 것처럼 들립니다 .

따라서 이미 작동하는 많은 코드가 있습니다. 그리고 왜 다른 사람의 구현으로 대체하고 싶을 지 궁금합니다.

타사 IoC 컨테이너를 고려한 전문가

  • 무료로 버그 수정
  • 도서관 디자인은 당신보다 낫다
  • 사람들은 이미 특정 라이브러리에 익숙 할 것입니다
  • 도서관은 당신보다 빠를 수 있습니다
  • 구현하고 싶지만 시간이 없었던 기능이있을 수 있습니다 (서비스 로케이터가 있습니까?)

단점

  • 무료로 버그가 소개됩니다 :)
  • 도서관 디자인은 당신보다 나빠질 수 있습니다
  • 새로운 API를 배워야합니다
  • 사용하지 않을 기능이 너무 많습니다
  • 일반적으로 작성하지 않은 코드를 디버깅하는 것이 더 어렵습니다.
  • 이전 IoC 컨테이너에서 마이그레이션하는 것은 지루할 수 있습니다

따라서 당신의 찬반을 당신의 단점과 비교하여 결정하십시오.


동의합니다 Sam-DI는 IoC의 한 유형이므로 원본 포스터가 DI를 수행하는 경우 이미 IoC를 수행하고 있으므로 실제 질문이 자신의 역할을하는 이유입니다.
Jamie Love

3
동의하지 않습니다. IOC는 DI를 수행하는 방법입니다. IOC 컨테이너는 동적 런타임 리플렉션을 사용하여 유형 인스턴스화를 제어하여 종속성을 충족시키고 주입함으로써 DI를 수행합니다. OP는 정적 DI를 수행하고 있으며 컴파일 타임 검사의 이점과 IOC의 동적 런타임 반영과 관련된 이점을 비교해야하는 이유를 고민하고 있습니다.
Maxm007

17

DI를 사용하면 IoC의 가치가 대부분 확보된다고 생각합니다. 이미 그렇게했기 때문에 나머지 혜택은 점진적입니다.

얻는 가치는 작업중인 응용 프로그램 유형에 따라 다릅니다.

  • 다중 테넌트의 경우 IoC 컨테이너는 다른 클라이언트 리소스를로드하기 위해 일부 인프라 코드를 처리 할 수 ​​있습니다. 클라이언트 고유의 구성 요소가 필요한 경우 사용자 지정 선택기를 사용하여 논리를 처리하고 클라이언트 코드에서 신경 쓰지 않아도됩니다. 이를 직접 구축 할 수 있지만 다음 은 IoC가 어떻게 도울 수 있는지에 대한 입니다.

  • 확장 성의 점이 많으면 IoC를 사용하여 구성에서 구성 요소를로드 할 수 있습니다. 이것은 일반적으로 빌드되지만 컨테이너가 도구를 제공합니다.

  • 교차 절단 문제에 대해 AOP를 사용하려는 경우 IoC는 메소드 호출을 가로 채기 위한 후크제공합니다 . 이는 프로젝트에서 일반적으로 수행되는 일이 드물지만 IoC를 사용하면 더 쉽습니다.

이전에 이와 같은 기능을 작성했지만 지금 이러한 기능 중 하나가 필요한 경우 아키텍처에 맞는 경우 사전 빌드되고 테스트 된 도구를 사용합니다.

다른 사람들이 언급했듯이 사용하려는 클래스를 중앙에서 구성 할 수도 있습니다. 이것이 좋은 일이기는하지만 잘못된 방향과 합병증이 발생합니다. 대부분의 응용 프로그램의 핵심 구성 요소는 많이 대체되지 않으므로 균형을 맞추기가 약간 더 어렵습니다.

나는 IoC 컨테이너를 사용하고 기능을 높이 평가하지만 절충점을 발견했다는 것을 인정해야한다. 내 코드는 클래스 수준에서 더 명확 해지고 응용 프로그램 수준에서 명확하지 않게된다 (즉, 제어 흐름 시각화).


16

나는 회복중인 IOC 중독자입니다. 요즘 대부분의 경우 DI에 IOC를 사용하는 것이 타당하다는 것을 알았습니다. IOC 컨테이너는 컴파일 시간 검사를 희생하고 대신에 "쉬운"설정, 복잡한 수명 관리 및 런타임시 종속성의 즉각적인 발견을 제공합니다. 컴파일 시간 확인 및 런타임 마술 / 예외의 손실을 발견하고, 대다수의 경우 종소리와 휘파람의 가치가 없습니다. 대기업 응용 프로그램에서는 진행중인 작업을 따르는 것이 매우 어려울 수 있습니다.

응용 프로그램에 추상 팩토리를 사용하고 객체 생성을 추상 팩토리에 종교적으로 지연시키는 것, 즉 적절한 DI를 수행하여 정적 설정을 매우 쉽게 중앙 집중화 할 수 있기 때문에 중앙 집중화 인수를 구입하지 않습니다.

정적 매직 프리 DI를 다음과 같이 사용하지 마십시오.

interface IServiceA { }
interface IServiceB { }
class ServiceA : IServiceA { }
class ServiceB : IServiceB { }

class StubServiceA : IServiceA { }
class StubServiceB : IServiceB { }

interface IRoot { IMiddle Middle { get; set; } }
interface IMiddle { ILeaf Leaf { get; set; } }
interface ILeaf { }

class Root : IRoot
{
    public IMiddle Middle { get; set; }

    public Root(IMiddle middle)
    {
        Middle = middle;
    }

}

class Middle : IMiddle
{
    public ILeaf Leaf { get; set; }

    public Middle(ILeaf leaf)
    {
        Leaf = leaf;
    }
}

class Leaf : ILeaf
{
    IServiceA ServiceA { get; set; }
    IServiceB ServiceB { get; set; }

    public Leaf(IServiceA serviceA, IServiceB serviceB)
    {
        ServiceA = serviceA;
        ServiceB = serviceB;
    }
}


interface IApplicationFactory
{
    IRoot CreateRoot();
}

abstract class ApplicationAbstractFactory : IApplicationFactory
{
    protected abstract IServiceA ServiceA { get; }
    protected abstract IServiceB ServiceB { get; }

    protected IMiddle CreateMiddle()
    {
        return new Middle(CreateLeaf());
    }

    protected ILeaf CreateLeaf()
    {
        return new Leaf(ServiceA,ServiceB);
    }


    public IRoot CreateRoot()
    {
        return new Root(CreateMiddle());
    }
}

class ProductionApplication : ApplicationAbstractFactory
{
    protected override IServiceA ServiceA
    {
        get { return new ServiceA(); }
    }

    protected override IServiceB ServiceB
    {
        get { return new ServiceB(); }
    }
}

class FunctionalTestsApplication : ApplicationAbstractFactory
{
    protected override IServiceA ServiceA
    {
        get { return new StubServiceA(); }
    }

    protected override IServiceB ServiceB
    {
        get { return new StubServiceB(); }
    }
}


namespace ConsoleApplication5
{
    class Program
    {
        static void Main(string[] args)
        {
            var factory = new ProductionApplication();
            var root = factory.CreateRoot();

        }
    }

    //[TestFixture]
    class FunctionalTests
    {
        //[Test]
        public void Test()
        {
            var factory = new FunctionalTestsApplication();
            var root = factory.CreateRoot();
        }
    }
}

컨테이너 구성은 추상 팩토리 구현이고 등록은 추상 멤버의 구현입니다. 새로운 싱글 톤 종속성이 필요한 경우 추상 팩토리에 다른 추상 속성을 추가하십시오. 일시적인 종속성이 필요한 경우 다른 방법을 추가하고 Func <>로 주입하십시오.

장점 :

  • 모든 설정 및 개체 생성 구성은 중앙 집중식입니다.
  • 구성은 단지 코드입니다
  • 컴파일 시간 확인을 통해 등록 업데이트를 잊을 수 없으므로 유지 관리가 쉽습니다.
  • 런타임 반영 마법 없음

회의론자들에게 다음 그린 필드 프로젝트를 진행하고 용기가 필요한 시점을 정직하게 물어 보는 것이 좋습니다. 팩토리 구현을 IOC 컨테이너 구성 모듈로 교체 할 때 나중에 IOC 컨테이너를 쉽게 고려할 수 있습니다.


이것은 IoC에 대한 논쟁이 아니라 구성 가능한 IoC 프레임 워크 에 대한 논쟁처럼 보이지 않습니다 . 여기 공장이 단순한 하드 코딩 된 IoC로 귀결되지 않습니까?
Mr.Mindor

포스터가 IoC를 언급했을 때 IoC 컨테이너 프레임 워크가 의도 된 것 같습니다. 원래 글을 써 주셔서 감사합니다. 이것은 프레임 워크없이 갈 수 있도록 설득하는 데 도움이됩니다. 테스트 및 프로덕션 코드에서 구성 할 수 있다면 "구성 가능한"프레임 워크의 장점은 무엇입니까?
huggie

용어에 대한 나의 이해에서, Inversion Of Control은 런타임에 의존성을 찾는 것을 의미합니다. 예, 팩토리는 기본적으로 하드 코딩 또는 정적 컨테이너이지만 IOC 컨테이너는 아닙니다. 컴파일 타임에 타입이 해결됩니다. 사전 조회를 사용하여 런타임에 유형을 확인하는 구성 가능한 프레임 워크를 사용하는 것에 대한 논쟁입니다.
Maxm007

14

나를 위해 IoC 컨테이너를 사용하는 가장 큰 이점은 (개인적으로 Ninject를 사용함) 설정 및 기타 종류의 전역 상태 객체를 전달하지 않는 것입니다.

나는 웹을 프로그래밍하지 않고, 콘솔 응용 프로그램이며 객체 트리의 많은 곳에서 객체 트리의 완전히 분리 된 분기에서 생성 된 사용자가 지정한 설정 또는 메타 데이터에 액세스해야합니다. IoC를 사용하면 Ninject에게 설정을 단일 톤으로 취급하도록 지시하고 (항상 하나의 인스턴스 만 있기 때문에) 생성자에서 설정 또는 사전을 요청하고 미리 설정하면 필요할 때 마술처럼 나타납니다!

IoC 컨테이너를 사용하지 않으면 필요한 객체가 실제로 사용하기 전에 2, 3, ..., n 개의 객체를 통해 설정 및 / 또는 메타 데이터를 전달해야합니다.

DI / IoC 컨테이너에는 다른 사람들이 자세히 설명하고 객체 생성이라는 아이디어에서 객체를 요청하는 아이디어로 전환하는 것이 마음에 들지 않을 수 있지만 DI를 사용하는 것이 나에게 도움이되었으므로 DI를 사용하는 것이 도움이 될 수 있습니다. 당신의 무기고에!


2
이것은 큰 장점입니다. 다른 사람이 이전에 언급하지 않았다는 것을 믿을 수 없습니다. 임시 객체를 전달하거나 저장할 필요없이 코드를 훨씬 깔끔하게 만들 수 있습니다.
Finglas

1
@qble 전역 객체는 훌륭하게 작동합니다 ... 코드베이스를 시간이 지남에 따라 수정하기가 점점 더 어려워지는 단단히 결합되고 테스트 할 수없는 악몽이되고 싶다면. 좋은 결과 내길 바랄 게.
Jeffrey Cameron

2
@JeffreyCameron IoC 컨테이너 전역 객체입니다. 전역 종속성을 제거하지는 않습니다.
qble

3
그러나 IoC 컨테이너는 나머지 응용 프로그램에 투명합니다. 시작할 때 한 번 호출하여 인스턴스를 얻은 다음 마지막으로 본 것입니다. 전역 객체를 사용하면 코드가 어려운 종속성으로 가득 차 있습니다.
Jeffrey Cameron

1
@qble은 팩토리를 사용하여 즉시 임시 인스턴스를 만듭니다. 공장은 싱글 톤 서비스입니다. 임시 인스턴스에 IOC 컨테이너의 무언가가 필요한 경우 팩토리에 연결하고 팩토리가 종속성을 임시 인스턴스에 전달하도록하십시오.
Jeffrey Cameron

14

원하는 경우 IoC 프레임 워크가 우수합니다 ...

  • ... 타입 안전을 버립니다. 모든 (모든?) IoC 프레임 워크는 모든 것이 올바르게 연결되어 있는지 확인하려는 경우 코드를 실행하도록합니다. "이봐! 모든 100 개의 클래스 초기화가 실패하여 null-pointer 예외가 발생하지 않도록 모든 것이 설정 되었으면 좋겠다."

  • ... 글로벌로 코드를 작성하십시오 (IoC 프레임 워크는 모두 글로벌 상태 변경에 관한 것입니다).

  • ... 무엇이 무엇인지에 대해 알 수 없으므로 리팩토링하기 어려운 불명확 한 종속성으로 엉터리 코드를 작성하십시오.

IoC의 문제점은이를 사용하는 사람들이 이와 같은 코드를 작성하는 데 사용된다는 것입니다.

public class Foo {
    public Bar Apa {get;set;}
    Foo() {
        Apa = new Bar();
    }
}

Foo와 Bar의 의존성이 결속되어 있기 때문에 분명히 결함이 있습니다. 그런 다음 코드를 작성하는 것이 더 낫다는 것을 깨달았습니다.

public class Foo {
    public IBar Apa {get;set;}
    Foo() {
        Apa = IoC<IBar>();
    }
}

그것은 또한 결함이 있지만 덜 분명합니다. Haskell에서는 유형 Foo()이 될 IO Foo것이지만 실제로는 IO-part를 원하지 않으며 디자인이 있으면 무언가 잘못되었다는 경고 신호입니다.

이를 없애기 위해 (IO- 파트), IoC- 프레임 워크의 모든 장점을 활용하십시오. 대신 추상 팩토리를 사용할 수있는 단점은 없습니다.

올바른 해결책은 다음과 같습니다.

data Foo = Foo { apa :: Bar }

아니면

data Foo = forall b. (IBar b) => Foo { apa :: b }

주사하십시오 (그러나 주사 라고는하지 않습니다) Bar.

또한 : Erik Meijer (LINQ의 발명가)와 함께이 비디오를보고 DI는 수학을 모르는 사람들 (그리고 더 이상 동의 할 수없는 사람들)을위한 것이라고 말합니다. http://www.youtube.com/watch?v = 8Mttjyf-8P4

Spolsky와 달리 저는 IoC 프레임 워크를 사용하는 사람들이 매우 똑똑하다고 생각하지 않습니다. 단순히 수학을 모른다고 생각합니다.


9
타입 안전을 버린다-당신의 예제가 여전히 타입 안전하다는 것을 제외하고는 그것이 인터페이스이다. 객체 유형을 전달하지 않고 인터페이스 유형을 전달합니다. 그리고 타입 안전은 null 참조 예외를 없애지 않습니다. null 안전 참조를 타입 안전 매개 변수로 전달할 수 있습니다. 이는 타입 안전 문제가 아닙니다.
blowdart

유형이 IoC 컨테이너에 연결되어 있는지 여부를 알 수 없기 때문에 유형 안전을 버립니다 (IoC <IBar> 유형이 IBar실제로 는 아닙니다 IO IBar). 예, Haskell 예에서 null 참조를 얻을 수 없습니다.
finnsson

유형 안전은 실제로 Java / C #에서는 연결되지 않은 객체에 관한 것이 아니라 단지 올바른 유형의 객체에 관한 것입니다. 그리고 ConcreteClass myClass = null은 여전히 ​​올바른 유형입니다. null이 아닌 경우 코드 계약이 적용됩니다.
blowdart

2
나는 당신의 첫 번째 포인트에 어느 정도 동의합니다. 나는 두 번째에 대한 설명을 원하며 세 번째는 결코 문제가되지 않았습니다. C # 샘플은 IoC 컨테이너를 서비스 로케이터로 사용합니다. 일반적인 실수입니다. 그리고 C # vs Haskell = 사과 대 오렌지 비교.
Mauricio Scheffer

2
타입 안전을 버리지 마십시오. 일부 (대부분은 아님) DI 컨테이너를 사용하면 등록 프로세스 후 전체 객체 그래프를 확인할 수 있습니다. 이 작업을 수행하면 응용 프로그램이 잘못 구성되어 시작되는 것을 막을 수 있으며 (실패) 내 응용 프로그램에는 항상 프로덕션 구성을 호출하는 몇 가지 단위 테스트가있어 테스트 중에 이러한 오류를 찾을 수 있습니다. IoC 컨테이너를 사용하는 모든 사람에게 모든 최상위 개체를 확인할 수있는 몇 가지 단위 테스트를 작성하도록 조언합니다.
Steven

10

Dependency Injection을 올바르게 구현하면 프로그래머가 코드의 테스트 가능성, 유연성, 유지 관리 성 및 확장 성을 개선하는 데 도움이되는 다양한 다른 프로그래밍 방법을 사용하는 경향이 있음을 발견했습니다. 단일 책임 원칙, 우려 분리 및 코딩과 같은 방법 API에 대한. 좀 더 모듈 식의 바이트 크기의 클래스와 메소드를 작성해야한다는 느낌이 들기 때문에 바이트 크기의 청크로 읽을 수 있기 때문에 코드를 더 쉽게 읽을 수 있습니다.

그러나 또한 수작업보다 프레임 워크를 통해 (특히 규칙을 사용하는 경우) 훨씬 쉽게 관리되는 종속성 트리를 생성하는 경향이 있습니다. 오늘 나는 LINQPad에서 무언가를 빠르게 테스트하고 싶었고 커널을 만들고 모듈에로드하는 것이 너무 귀찮다는 것을 알았습니다.

var merger = new SimpleWorkflowInstanceMerger(
    new BitFactoryLog(typeof(SimpleWorkflowInstanceMerger).FullName), 
    new WorkflowAnswerRowUtil(
        new WorkflowFieldAnswerEntMapper(),
        new ActivityFormFieldDisplayInfoEntMapper(),
        new FieldEntMapper()),
    new AnswerRowMergeInfoRepository());

돌이켜 보면 IoC 프레임 워크를 사용하는 것이 더 빠를 것입니다. 모듈은 규칙에 따라 거의 모든 것을 정의하기 때문입니다.

이 질문에 대한 답변과 의견을 연구하는 데 시간을 보냈지 만 IoC 컨테이너 사용에 반대하는 사람들은 진정한 의존성 주입을 연습하지 않는다고 확신합니다. 내가 본 예제는 일반적으로 의존성 주입과 혼동되는 사례입니다. 어떤 사람들은 코드를 읽는 데 어려움이 있다고 불평합니다. 올바르게 수행하면 IoC 컨테이너를 사용할 때와 직접 DI를 사용할 때 코드의 대부분이 동일해야합니다. 차이점은 전적으로 응용 프로그램 내에서 "시작 지점"에 있어야합니다.

즉, IoC 컨테이너가 마음에 들지 않으면 Dependency Injection을 수행하지 않는 것입니다.

또 다른 요점 : 어디서나 리플렉션을 사용하면 Dependency Injection을 직접 수행 할 수 없습니다. 내비게이션을 코딩하는 데 반영되는 것이 싫지만 실제로 피할 수없는 특정 영역이 있음을 인식해야합니다. 예를 들어 ASP.NET MVC는 각 요청에 대한 반영을 통해 컨트롤러를 인스턴스화하려고 시도합니다. 의존성 주입을 직접 수행하려면 모든 컨트롤러 를 "컨텍스트 루트" 로 만들어야 합니다 .

public class MyController : Controller
{
    private readonly ISimpleWorkflowInstanceMerger _simpleMerger;
    public MyController()
    {
        _simpleMerger = new SimpleWorkflowInstanceMerger(
            new BitFactoryLog(typeof(SimpleWorkflowInstanceMerger).FullName), 
            new WorkflowAnswerRowUtil(
                new WorkflowFieldAnswerEntMapper(),
                new ActivityFormFieldDisplayInfoEntMapper(),
                new FieldEntMapper()),
            new AnswerRowMergeInfoRepository())
    }
    ...
}

이제 이것을 DI 프레임 워크에서 허용하는 것과 비교해보십시오 :

public MyController : Controller
{
    private readonly ISimpleWorkflowInstanceMerger _simpleMerger;
    public MyController(ISimpleWorkflowInstanceMerger simpleMerger)
    {
        _simpleMerger = simpleMerger;
    }
    ...
}

DI 프레임 워크를 사용하여 다음을 참고하십시오.

  • 이 수업을 단위 테스트 할 수 있습니다. mock을 만들어서 ISimpleWorkflowInstanceMerger데이터베이스 연결이나 그 밖의 어떤 것도 필요없이 예상 한대로 사용 되는지 테스트 할 수 있습니다.
  • 훨씬 적은 코드를 사용하므로 코드를 훨씬 쉽게 읽을 수 있습니다.
  • 종속성의 종속성 변경 중 하나 인 경우 컨트롤러를 변경할 필요가 없습니다. 여러 컨트롤러가 동일한 종속성 중 일부를 사용한다고 생각할 때 특히 좋습니다.
  • 데이터 레이어에서 클래스를 명시 적으로 참조하지 않습니다. 내 웹 응용 프로그램에는 ISimpleWorkflowInstanceMerger인터페이스가 포함 된 프로젝트에 대한 참조 만 포함될 수 있습니다 . 이를 통해 응용 프로그램을 별도의 모듈로 분리하고 진정한 다중 계층 아키텍처를 유지함으로써 훨씬 유연하게 만들 수 있습니다.

일반적인 웹 응용 프로그램에는 꽤 많은 컨트롤러가 있습니다. 각 컨트롤러에서 직접 DI를 수행하는 데 따르는 모든 고통은 어플리케이션이 성장함에 따라 실제로 더해집니다. 리플렉션을 통해 서비스를 인스턴스화하지 않는 컨텍스트 루트가 하나 뿐인 애플리케이션이있는 경우 큰 문제가되지 않습니다. 그럼에도 불구하고 Dependency Injection을 사용하는 응용 프로그램은 종속성 그래프를 관리하기 위해 어떤 종류의 프레임 워크를 사용하지 않는 한 특정 크기에 도달하면 관리하는 데 비용이 많이 듭니다.


9

"new"키워드를 사용할 때마다 구체적인 클래스 종속성이 생성되고 약간의 경고음이 발생합니다. 이 개체를 격리하여 테스트하기가 더 어려워집니다. 해결책은 인터페이스를 프로그래밍하고 종속성을 주입하여 해당 인터페이스를 구현하는 모든 대상 (예 : 모의)으로 객체를 단위 테스트 할 수 있도록하는 것입니다.

문제는 어딘가에 객체를 만들어야한다는 것입니다. 팩토리 패턴은 POXO에서 커플 링을 전환하는 한 가지 방법입니다 (일반적인 "OO 언어를 여기에 삽입하십시오"오브젝트). 귀하와 동료가 모두 이와 같은 코드를 작성하는 경우 IoC 컨테이너는 다음 "증가 개선"으로 코드베이스에 적용 할 수 있습니다. 그것은 모든 불쾌한 Factory 상용구 코드를 깨끗한 객체와 비즈니스 로직에서 벗어나게 할 것입니다. 그들은 그것을 얻을 사랑합니다. 도대체 왜 당신이 그것을 사랑하고 모든 사람을 열광시키는 지에 대해 이야기하십시오.

동료들이 아직 DI를하고 있지 않다면, 먼저 그 부분에 초점을 두는 것이 좋습니다. 쉽게 테스트 할 수있는 깨끗한 코드를 작성하는 방법에 대해 널리 알리십시오. 깨끗한 DI 코드는 어려운 부분입니다. 일단 객체 배선 로직을 팩토리 클래스에서 IoC 컨테이너로 옮기는 것은 비교적 쉽지 않습니다.


8

모든 종속성이 명확하게 표시되기 때문에 느슨하게 결합되고 동시에 응용 프로그램에서 쉽게 액세스하고 재사용 할 수있는 구성 요소를 만들 수 있습니다.


7

IoC 컨테이너 가 필요 하지 않습니다 .

그러나 DI 패턴을 엄격하게 따르는 경우, 하나의 중복 된 지루한 코드를 제거 할 수 있습니다.

어쨌든 라이브러리 / 프레임 워크를 사용하는 가장 좋은시기입니다. 어떻게하고 있는지 이해하고 라이브러리없이 할 수 있습니다.


6

나는 방금 자란 DI 코드를 잡아 당겨 IOC로 교체하는 과정에 처해 있습니다. 아마도 200 줄 이상의 코드를 제거하고 약 10으로 대체했을 것입니다. 예, 컨테이너 (Winsor) 사용 방법에 대해 약간의 학습을해야했지만 인터넷 기술을 연구하는 엔지니어입니다. 21 세기에 익숙해 져 있습니다. 방법을 살펴 보는 데 약 20 분이 걸렸을 것입니다. 이것은 내 시간의 가치가 있었다.


3
"하지만 저는 21 세기 인터넷 기술을 연구하는 엔지니어입니다."-> +1
user96403

5

클래스를 계속 분리하고 종속성을 반전시키면서 클래스는 계속 작게 유지되고 "종속성 그래프"는 계속 커집니다. (나쁘지는 않습니다.) IoC 컨테이너의 기본 기능을 사용하면 이러한 모든 객체를 간단하게 배선 할 수 있지만 수동으로 수행하면 매우 부담이 될 수 있습니다. 예를 들어 "Foo"의 새 인스턴스를 만들고 싶지만 "Bar"가 필요한 경우 어떻게해야합니까? "Bar"에는 "A", "B"및 "C"가 필요합니다. 그리고 그들 각각은 3 가지 다른 것들 등이 필요합니다 (예, 좋은 가짜 이름을 만들 수는 없습니다 :)).

IoC 컨테이너를 사용하여 객체 그래프를 작성하면 톤을 복잡하게 줄이고 일회성 구성으로 푸시 할 수 있습니다. 나는 단순히 "나를 'Foo'를 만들어라"라고 말하고 하나를 만드는 데 무엇이 필요한지 알아냅니다.

어떤 사람들은 훨씬 더 많은 인프라에 IoC 컨테이너를 사용하는데, 이는 고급 시나리오에는 적합하지만 새 개발자를 위해 난독 화하고 코드를 읽고 디버깅하기가 어렵다는 데 동의합니다.


1
왜 우리는 공통 분모를 끌어 올리는 대신 가장 낮은 분모를 계속 달래는가?
stevenharman

4
나는 적용에 대해 아무 말도하지 않았다. 방금 고급 시나리오로 인해 새로운 개발자에게 더 난해 할 수 있다고 말했습니다. 이것은 사실입니다. 나는 "그렇게하지 말라"고 말하지 않았습니다. 그러나 그때도 (악마의 옹호자를위한 시간) 코드베이스를보다 명확하고 접근하기 쉽게 만드는 동일한 방법을 수행하는 다른 방법이 종종 있습니다. 타당한 이유가 없기 때문에 인프라 도구 사용자 지정 구성의 복잡성을 숨기고 아무도 끌어 올리지 않습니다.
Aaron Lerch

4

Unity에 대한 차이점. 너무 커지면 서까래에서 삐걱 거리는 소리를들을 수 있습니다.

사람들이 C ++의 템플릿이 90 년대로 돌아가는 우아한 방법에 대해 이야기했던 IoC 코드가 깨끗한 종류의 사람들과 같은 종류의 사람들에 대해 분개하기 시작할 때 결코 놀라지 않습니다. . 바!


2

.NET 세계에서 AOP는 그다지 인기가 없으므로 DI의 경우 프레임 워크는 사용자가 직접 작성하거나 다른 프레임 워크를 사용하는 유일한 옵션입니다.

AOP를 사용한 경우 애플리케이션 컴파일시 삽입 할 수 있으며 이는 Java에서 더 일반적입니다.

커플 링 감소와 같이 DI에 많은 이점이있어 단위 테스트가 더 쉬워 지지만 어떻게 구현할 수 있습니까? 리플렉션을 사용하여 직접 수행 하시겠습니까?


새로운 MEF 프레임 워크는 .NET에서만 가능하며 코드가 더 깨끗하고 이해하기 쉬워 DI 개념을 훨씬 쉽게 이해할 수 있어야합니다.
terjetyl

4
@TT : MEF는 없다 의존성 주입 용기 AOP 무관하다.
Mauricio Scheffer

2

거의 3 년이 지났습니다.

  1. IoC 프레임 워크에 찬성하여 의견을 말한 사람들의 50 %는 IoC와 IoC 프레임 워크의 차이점을 이해하지 못합니다. 앱 서버에 배포하지 않고도 코드를 작성할 수 있다는 것을 알고 있습니다.

  2. 가장 널리 사용되는 Java Spring 프레임 워크를 취한다면, XMl에서 코드로 옮겨진 IoC 구성이며 이제 다음과 같이 보입니다.

`@Configuration 공개 클래스 AppConfig {

public @Bean TransferService transferService() {
    return new TransferServiceImpl(accountRepository());
}

public @Bean AccountRepository accountRepository() {
    return new InMemoryAccountRepository();
}

}`그리고 우리는 왜 이것을 정확히하기 위해 프레임 워크가 필요합니까?


1

솔직히 IoC 컨테이너가 필요한 경우가 많지 않으며 대부분의 경우 불필요한 복잡성을 추가합니다.

객체의 구성을보다 간단하게 만들기 위해 사용하는 경우 여러 장소 에서이 객체를 인스턴스화합니까? 싱글 톤이 당신의 필요에 맞지 않습니까? 런타임에 구성을 변경하고 있습니까? (데이터 소스 유형 전환 등).

그렇다면 IoC 컨테이너가 필요할 수 있습니다. 그렇지 않다면 개발자가 쉽게 볼 수있는 위치에서 초기화를 옮기는 것입니다.

어쨌든 인터페이스가 상속보다 낫다고 누가 말했습니까? 서비스를 테스트한다고 가정 해보십시오. 생성자 DI를 사용하지 않고 상속을 사용하여 종속성 모의를 작성하지 않는 이유 내가 사용하는 대부분의 서비스에는 종속성이 거의 없습니다. 쓸모없는 인터페이스와 수단의 톤을 유지하는이 방법의 방지를 테스트 장치를 이렇게하면 당신은하지 않습니다 빨리에 ReSharper에서를 사용하는 방법의 선언을 찾을 수 있습니다.

나는 대부분의 구현에서 IoC 컨테이너가 불필요한 코드를 제거한다고 말하는 것은 신화라고 생각합니다.

먼저 컨테이너를 먼저 설정합니다. 그런 다음 초기화해야 할 각 개체를 정의해야합니다. 따라서 초기화시 코드를 저장하지 않고 객체를 두 번 이상 사용하지 않는 한 코드를 이동하십시오 (싱글 톤으로 더 좋습니까?). 그런 다음이 방법으로 초기화 한 각 개체에 대해 인터페이스를 만들고 유지 관리해야합니다.

아무도 이것에 대해 생각이 있습니까?


소규모 프로젝트의 경우에도 XML로 구성을 구성하면 훨씬 깨끗하고 모듈화됩니다. 처음으로 더 이상 접착제로 오염되지 않기 때문에 코드를 재사용 할 수 있습니다. 올바른 소프트웨어 제품의 경우, 변경된 XML 파일을 하나만 배포 하여 정확히 동일한 코드베이스 내에서ShippingCostCalculator 또는 ProductUIPresentation, 또는 다른 전략 / 구현을 구성하거나 바꿀 수 있습니다 .
Thomas W

이것이 일반적인 방법으로 willy-nilly로 구현된다면 복잡성을 더 많이 추가한다고 생각합니다. 런타임에 무언가를 교환해야 할 때 훌륭하지만 난독 화를 추가하는 이유는 무엇입니까? 전환되지 않는 항목에 대해 추가 인터페이스의 오버 헤드를 유지하는 이유는 무엇입니까? 테스트에 IoC를 사용하지 않는다고 말하는 것은 아닙니다. 꼭 속성이나 생성자 의존성 주입을 사용하십시오. 인터페이스를 사용해야합니까? 왜 테스트 객체를 서브 클래 싱하지 않습니까? 더 깨끗하지 않습니까?
Fred

XML 구성 / IoC를 수행하기 위해 인터페이스가 전혀 필요하지 않습니다. ~ 8 페이지와 5 또는 6 서비스 만있는 작은 프로젝트에서 명확성이 크게 향상되었습니다. 거기에 덜 난처 서비스 및 컨트롤러 더 이상 필요하지 글루 코드 보낸 사람.
Thomas W

1

종속성 구성을 중앙 집중화하여 전체적으로 쉽게 교체 할 수 있도록하려면 IoC 컨테이너가 필요합니다. 이것은 많은 종속성이 교환되지만 종속성 간의 상호 의존성이 거의없는 TDD에서 가장 의미가 있습니다. 이는 객체 구성의 제어 흐름을 어느 정도 난독 화시키는 비용으로 수행되므로 잘 구성되고 합리적으로 문서화 된 구성이 중요합니다. 이것을하는 이유도있는 것이 좋으며, 그렇지 않으면 단지 금도금 추상화 일뿐 입니다. 나는 그것이 너무 잘못하여 생성자에 대한 goto 문과 동등한 것으로 드래그 다운되는 것을 보았습니다.


1

이유는 다음과 같습니다 . 이 프로젝트를 IOC-with-Ninject라고합니다. Visual Studio에서 다운로드하여 실행할 수 있습니다. 이 예제는 Ninject를 사용하지만 모든 'new'문은 한 위치에 있으며 사용할 바인드 모듈을 변경하여 애플리케이션 실행 방법을 완전히 변경할 수 있습니다. 이 예제는 모의 버전의 서비스 또는 실제 버전에 바인딩 할 수 있도록 설정되었습니다. 중요하지 않은 작은 프로젝트에서는 큰 프로젝트에서는 큰 문제입니다.

분명히 말하면, 내가 볼 때 장점 : 1) 코드 루트의 한 위치에서 모든 새로운 진술. 2) 한 번의 변경으로 코드를 완전히 리팩터링하십시오. 3) '쿨 팩터'에 대한 추가 포인트는 '잘. :피


1

나는 내 관점에서 IOC가 좋지 않은 이유를 찾으려고 노력할 것입니다.

다른 모든 것들과 마찬가지로, IOC 컨테이너 (또는 아인슈타인이 I = OC ^ 2)는 코드에서 필요한지 여부를 스스로 결정해야하는 개념입니다. IOC에 대한 최근의 패션 외침은 단지 패션입니다. 패션에 빠지지 마십시오. 코드에서 구현할 수있는 수많은 개념이 있습니다. 우선, 나는 프로그래밍을 시작한 이후 의존성 주입을 사용하고 있으며 그 이름으로 대중화 될 때 그 용어 자체를 배웠습니다. 의존성 제어는 매우 오래된 주제이며 무엇으로부터 분리되었는지에 따라 지금까지 수조 가지 방법으로 해결되었습니다. 모든 것에서 모든 것을 분리하는 것은 말도 안됩니다. IOC 컨테이너의 문제점은 Entity Framework 또는 NHibernate만큼 유용하다는 것입니다. 데이터베이스를 시스템에 연결해야하는 즉시 객체 관계형 매퍼를 작성하는 것이 필수적이지만, IOC 컨테이너가 항상 필요한 것은 아닙니다. 따라서 IOC 컨테이너가 유용한 경우 :

  1. 많은 의존성이있는 상황이있을 때 정리하고 싶습니다.
  2. 타사 제품과 코드를 연결하지 않아도되는 경우
  3. 개발자가 새로운 도구로 작업하는 방법을 배우고 싶을 때

1 : 코드에 너무 많은 의존성이 있거나 디자인 초기에 그것들을 알고있는 것은 아닙니다. 추상적 사고는 추상적 사고가 필요할 때 유용합니다.

2 : 코드를 타사 코드와 연결하는 것은 HuGe 문제입니다. 나는 10 년 이상 된 코드로 작업하고 있었고 당시에는 고급 개념 ATL, COM, COM + 등을 따르고있었습니다. 이 코드로 할 수있는 일은 없습니다. 내가 말하는 것은 고급 개념이 명백한 이점을 제공하지만 오래된 이점 자체로 장기적으로 취소된다는 것입니다. 그것은 단지 더 비싸게 만들었습니다.

3 : 소프트웨어 개발이 충분하지 않습니다. 고급 개념을 코드로 자르도록 허용하면 인식 할 수없는 수준으로 확장 할 수 있습니다. IOC2에 문제가 있습니다. 종속성을 분리하지만 논리 흐름도 분리합니다. 버그를 발견했으며 상황을 조사하기 위해 휴식을 설정해야한다고 상상해보십시오. 다른 고급 개념 인 IOC2는이를 더욱 어렵게 만들고 있습니다. 버그를 수정하면 개념을 다시 준수해야하기 때문에 개념 내에서 버그를 수정하는 것은 일반 코드에서 버그를 수정하는 것보다 어렵습니다. (예를 들어 C ++ .NET은 구문을 너무 많이 변경하여 구 버전의 .NET을 리팩터링하기 전에 열심히 생각해야합니다.) IOC의 문제점은 무엇입니까? 문제는 종속성을 해결하는 데 있습니다. 해결 논리는 일반적으로 IOC2 자체에 숨겨져 있습니다. 배우고 유지해야하는 드문 방식으로 작성 될 수 있습니다. 타사 제품이 5 년 안에 있습니까? 마이크로 소프트는 아니었다.

"우리는 IOC2에 관한 모든 곳에서"어떻게 "증후군이 기록되는지 알고 있습니다. 이것은 자동화 테스트와 유사합니다. 언뜻보기에 멋진 용어와 완벽한 솔루션, 당신은 단순히 모든 테스트를 밤새 실행하고 아침에 결과를 볼 수 있습니다. 자동화 된 테스트가 실제로 의미하는 바를 회사마다 설명하는 것은 정말 고통 스럽습니다. 자동화 된 테스트는 제품의 품질을 높이기 위해 밤새 도입 할 수있는 버그 수를 줄이는 빠른 방법이 아닙니다. 그러나 패션은 그 개념을 귀찮게 지배하고 있습니다. IOC2는 동일한 증후군을 앓고 있습니다. 소프트웨어가 제대로 작동하려면 소프트웨어를 구현해야합니다. 최근 인터뷰에서 IOC2 및 자동화를 구현하고 있는지 묻습니다. 그것은 패션의 징조입니다. 회사는 MFC로 작성된 코드의 일부를 포기하지 않을 것입니다.

소프트웨어의 다른 개념으로 IOC2를 배워야합니다. IOC2를 사용해야하는지에 대한 결정은 팀과 회사 내에서 이루어집니다. 그러나 결정을 내리기 전에 적어도 위의 모든 주장을 언급해야합니다. 플러스 쪽이 마이너스 쪽을 능가하는 경우에만 긍정적 인 결정을 내릴 수 있습니다.

IOC2에는 해결되는 문제 만 해결하고 도입 한 문제를 소개한다는 점을 제외하고는 아무 문제가 없습니다. 다른 건 없어 그러나 패션에 반대하는 것은 매우 어렵습니다. 그들의 환상에 관한 문제가 명백해질 때 그들 중 아무도없는 것이 이상하다. 소프트웨어 산업의 많은 개념은 이익을 창출하고, 서적을 작성하고, 회의를 개최하고, 신제품을 만들었으므로 방어되었습니다. 그것은 패션, 보통 짧은 수명입니다. 사람들이 다른 것을 발견하자마자 완전히 버립니다. IOC2는 유용하지만 다른 많은 사라진 개념과 같은 신호를 보여줍니다. 그것이 살아남을지 모르겠다. 이에 대한 규칙은 없습니다. 유용하다고 생각하면 살아남을 것입니다. 아니, 그런 식으로 가지 않습니다. 하나의 큰 부유 한 회사이면 충분하며 개념은 몇 주 안에 죽을 수 있습니다. 우리는 볼 수 있습니다. NHibernate는 살아 남았고 EF는 2 위를 차지했습니다. 아마도 IOC2도 살아남을 것입니다. 소프트웨어 개발에서 대부분의 개념은 특별한 것이 아니며 매우 논리적이고 단순하며 명백하며 때로는 개념 자체를 이해하는 것보다 현재 명명 규칙을 기억하기가 더 어렵다는 것을 잊지 마십시오. IOC2에 대한 지식이 개발자를 더 나은 개발자로 만들었습니까? 개발자가 IOC2와 유사한 개념을 생각 해낼 수 없다면 IOC2가 어떤 문제를 해결하고 있는지 이해하기 어려울 것입니다. 정치적으로 옳다는 이유로 소프트웨어 개발에서 대부분의 개념은 특별한 것이 아니며 매우 논리적이고 단순하며 명백하며 때로는 개념 자체를 이해하는 것보다 현재 명명 규칙을 기억하기가 더 어렵다는 것을 잊지 마십시오. IOC2에 대한 지식이 개발자를 더 나은 개발자로 만들었습니까? 개발자가 IOC2와 유사한 개념을 생각 해낼 수 없다면 IOC2가 어떤 문제를 해결하고 있는지 이해하기 어려울 것입니다. 정치적으로 옳다는 이유로 소프트웨어 개발에서 대부분의 개념은 특별한 것이 아니며 매우 논리적이고 단순하며 명백하며 때로는 개념 자체를 이해하는 것보다 현재 명명 규칙을 기억하기가 더 어렵다는 것을 잊지 마십시오. IOC2에 대한 지식이 개발자를 더 나은 개발자로 만들었습니까? 개발자가 IOC2와 유사한 개념을 생각 해낼 수 없다면 IOC2가 어떤 문제를 해결하고 있는지 이해하기 어려울 것입니다. 정치적으로 옳다는 이유로 IOC2에 대한 지식이 개발자를 더 나은 개발자로 만들었습니까? 개발자가 IOC2와 유사한 개념을 생각 해낼 수 없다면 IOC2가 어떤 문제를 해결하고 있는지 이해하기 어려울 것입니다. 정치적으로 옳다는 이유로 IOC2에 대한 지식이 개발자를 더 나은 개발자로 만들었습니까? 개발자가 IOC2와 유사한 개념을 생각 해낼 수 없다면 IOC2가 어떤 문제를 해결하고 있는지 이해하기 어려울 것입니다. 정치적으로 옳다는 이유로


0

개인적으로 IoC를 내 응용 프로그램의 일종의 구조 맵으로 사용합니다 (예, StructureMap;도 선호합니다). 테스트 중에 일반적인 인터페이스 구현을 Moq 구현으로 쉽게 대체 할 수 있습니다. 테스트 설정을 만드는 것은 IoC 프레임 워크에 새로운 초기화 호출을하는 것만 큼 쉬울 수 있습니다.

이것은 아마도 IoC가 아닌 것이지만, 내가 가장 많이 사용하는 것으로 생각합니다 ..




0

나는 이것이 다소 오래된 게시물이라는 것을 알고 있지만 여전히 합리적으로 활성화되어있는 것 같으며 아직 다른 답변에 언급되지 않은 몇 가지 사항을 기여할 것이라고 생각했습니다.

나는 의존성 주입의 이점에 동의한다고 말하지만 Maxm007의 대답과 다른 패턴을 사용하여 직접 객체를 구성하고 관리하는 것을 선호합니다. 타사 컨테이너를 사용할 때 두 가지 주요 문제가 있습니다.

1) 타사 라이브러리를 사용하여 개체의 수명을 "자동으로"관리하면 예기치 않은 결과가 발생할 수 있습니다. 특히 대규모 프로젝트에서는 예상보다 훨씬 더 많은 사본을 보유 할 수 있으며 수명주기를 수동으로 관리하는 경우보다 더 많은 사본을 보유 할 수 있습니다. 이것이 사용 된 프레임 워크에 따라 다르지만 문제는 여전히 존재합니다. 개체가 때때로 예상보다 오래 살 수 있기 때문에 개체에 리소스, 데이터 연결 등이 있으면 문제가 될 수 있습니다. 따라서 IoC 컨테이너는 응용 프로그램의 리소스 사용률과 메모리 공간을 늘리는 경향이 있습니다.

2) IoC 컨테이너는 "블랙 박스 프로그래밍"의 한 형태라고 생각합니다. 특히 경험이 적은 개발자가 개발자를 남용하는 경향이 있음을 알게되었습니다. 프로그래머가 객체를 서로 관련시키는 방법이나 분리하는 방법에 대해 생각할 필요가 없습니다. 왜냐하면 얇은 공기에서 원하는 객체를 간단하게 잡을 수있는 메커니즘을 제공하기 때문입니다. 예를 들어 ObjectA가 ObjectB에 대해 직접 알 필요가없는 좋은 설계 이유가있을 수 있지만, 경험이없는 프로그래머는 팩토리 나 브리지 또는 서비스 로케이터를 만드는 대신 "문제가 없습니다. IoC 컨테이너에서 ObjectB를 가져옵니다." ". 이것은 실제로 객체 결합을 증가시킬 수 있으며, 이는 IoC가 방지하는 데 도움이됩니다.


-8

ASP.NET 프로젝트의 Dependency Injection은 몇 줄의 코드로 수행 할 수 있습니다. 여러 프런트 엔드를 사용하고 단위 테스트가 필요한 앱이있는 경우 컨테이너를 사용하면 이점이 있다고 가정합니다.


1
예를 들어 줄 수 있습니까? 다른 응용 프로그램 유형과 비교하여 ASP.NET에서 DI는 어떻게 다른가요?
tofi9 2019
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.