간단한 DI 코드가 아닌 IoC 컨테이너가 필요한 이유는 무엇입니까? [닫은]
나는 한동안 DI ( Dependency Injection )를 사용하여 생성자, 속성 또는 메소드에 생성했습니다. IoC ( Inversion of Control ) 컨테이너 를 사용할 수 있습니다 . 하지만 더 많이 읽을수록 커뮤니티에서 IoC 컨테이너를 사용하는 것이 더 커집니다.
StructureMap , Ninject에 , 유니티 및 Funq 와 같은 .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());
휙! 모든 수동 INotifyPropertyChanged BS는 이제 해당 개체의 모든 가상 속성 설정자에서 자동으로 생성됩니다.
이게 마법인가요? 예 ! 이 코드가 제 역할을한다는 사실을 점보 할 수있는 점보를 래핑하는 모든 속성을 건너 뛸 수 있습니다. 해결해야 할 비즈니스 문제가 있습니다.
AOP를 수행하는 IoC 도구의 다른 흥미로운 사용 :
- 선언적 및 중첩 트랜잭션
- 선언 및 중첩 작업 단위
- 벌채 반출
- 사전 / 사후 조건 (계약 별 설계)
당신과 함께 있어요, 바딤. IoC 컨테이너는 간단하고 우아하며 유용한 개념을 취하며 200 페이지의 매뉴얼을 통해 이틀 동안 공부해야 할 만듭니다.
개인적으로 IoC 커뮤니티가 Martin Fowler 의 아름답고 우아한 기사를 가져와 일반적으로 200-300 페이지의 매뉴얼이있는 복잡한 프레임 워크로 바꾼 방법에 대해 당황합니다 .
나는 비판적이지 않고 노력하지만 (HAHA!) IoC 컨테이너를 사용하는 사람들은 (A) 매우 똑똑하고 (B) 똑똑하지 않은 사람들에 대한 공감이 부족하다고 생각합니다. 그들에게는 모든 것이 완벽하게 이해되기 때문에 많은 일반 프로그래머가 혼란스럽게 생각한다는 것을 이해하는 데 어려움을 겪습니다. 그것은 지식 의 저주입니다 . IoC 컨테이너를 이해하는 사람들은 그것을 이해하지 못하는 사람들이 믿기 어렵습니다.
IoC 컨테이너 사용의 가장 가까운 곳은 테스트 모드와 구성 모드 사이를 아름답게 수있는 스위치 것입니다. 예를 들어, 데이터베이스 액세스 클래스의 두 가지 버전이 가정 해 보겠습니다. 하나는 적극적으로 로깅하고 많은 유효성 검사를 수행하고 개발 중에 버전이고 다른 버전은 로깅 또는 유효성 검사가없는에서 엄청나게 빠 사용합니다. 한 곳에서 전환 할 수있는 것이 좋습니다. 반면에 이것은 IoC 컨테이너의 기본적인 문제없이 쉽게 처리 할 수있는 매우 사소한입니다.
IoC 컨테이너를 사용하면 솔직히 코드를 읽기가 훨씬 더 어려워 진다고 생각합니다. 코드가 수행하려는 작업을하기 위해 수행하는 위치의 수는 최소한 한 개씩 증가합니다. 그리고 천국 어딘가에서 천사가 외가 있습니다.
아마도 아무도 DI 컨테이너 프레임 워크를 사용하도록 강요하지 않을 것입니다. 이미 DI를 사용하여 클래스를 분리하고 테스트 가능성을 개선하고 많은 이점을 가지고 있습니다. 요컨대, 일반적으로 좋은 점인 단순함을 선호합니다.
시스템이 수동 DI가 잡일 (즉, 유지 관리 증가)이되는 수준에 도달하면 DI 프레임 워크의 팀 학습 곡선과 비교해.
수명 관리를 더 많이 제어해야하는 경우 (즉, Singleton 패턴을 구현해야한다고 생각하는 경우) DI 컨테이너를 제거해야합니다.
DI 컨테이너를 사용하는 경우 필요한 기능 만 사용하십시오. XML 구성 파일을 건너 뛰고 충분하면 코드에서 구성하십시오. 생성자 작성을 고수하십시오. Unity 또는 StructureMap의 기본 사항은 몇 페이지로 압축 할 수 있습니다.
Mark Seemann의 훌륭한 블로그 게시물이 있습니다. DI 컨테이너를 사용하는 경우
제 생각에 IoC의 가장 큰 이점은 구성을 중앙 집중화 할 수있는 것입니다.
현재 사용중인 경우 코드는 다음과 같을 수 있습니다.
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 컨테이너 의 개념 이 나쁘다고 생각하지 않습니다 . 그러나 구현은 다양한 응용 프로그램에서 유용하지만 간단하고 쉽게 제거해야합니다 (즉, 유연해야합니다).
아마 1 개 중 6 개와 나머지 6 개일 것입니다. 실제 응용 프로그램 (장난감이나 데모가 아님)은 복잡한 경우가 많고 규칙에 대한 예외가. 캡슐화 할 명령형 코드로 캡슐화합니다. 하지만 어딘가에 그것을 표현해야합니다.
컨테이너를 사용하는 것은 주로 명령형 / 펼쳐 적 스타일의 초기화 및 구성에서 선언적 스타일로 변경하는 것입니다. 이럴 수 있습니다.
- 감소 털 메인 프로그램 시작 루틴을.
- 상당히 심층적 인 배포 시간 내에 기능을 지원합니다.
- 의존성 배치 스타일을 새로운 작업에 대한 저항이 가장 약간 경로로 만듭니다.
물론 어려움이있을 수 있습니다.
- 복잡한 시작 / 종료 / 수명주기 관리가 필요한 코드는 컨테이너에 쉽게 설치할 수 없습니다.
- 개인, 프로세스 및 팀 문화 문제를 탐색해야 할 것입니다.하지만 그래서 질문을했습니다.
- 일부 툴은 신속하게 자체적으로 무거워지고, 많은 DI 컨테이너가 반발로 포장 된 깊은 패키지를 장려합니다.
마틴 파울러가 다양한 패턴을 사용하여 이미 자신의 IoC 컨테이너를 구축 하고 다른 사람의 구현이 자신보다 나은 이유를 많은 것처럼 들립니다 .
따라서 이미 작동하는 코드가 많이 있습니다. 그리고 다른 사람의 구현으로 대체하려는 이유가 궁금합니다.
IoC 컨테이너를 고려하는 장점
- 무료로 버그 수정
- 도서관 디자인이 당신보다 나을 수 있습니다
- 사람들은 이미 특정 라이브러리에 익숙 할 수 있습니다.
- 도서관이 당신보다 빠를 수 있습니다
- 구현하고 싶지만 시간이 없던 일부 기능이있을 수 있습니다 (서비스 로케이터가 있습니까?).
단점
- 무료로 버그가 소개됩니다. :)
- 도서관 디자인이 당신보다 나쁠 수 있습니다
- 새로운 API를 배워야합니다
- 사용하지 않을 기능이 너무 많습니다.
- 작성하지 않은 코드를 디버그하는 것이 일반적으로 더 어렵습니다.
- 이전 IoC 컨테이너에서 마이그레이션하는 것은 지루할 수 있습니다.
단점과 단점을 비교하고 결정을 내리십시오.
IoC의 가치는 대부분 DI를 사용하여 얻을 수 있다고 생각합니다. 이미 그렇게하고 있기 때문에 나머지 혜택은 점진적입니다.
얻을 수있는 가치는 작업중 인 애플리케이션 유형에 따라 달라집니다.
다중 테넌트의 경우 IoC 컨테이너는 다른 클라이언트 리소스를로드하기위한 일부 인프라 코드를 처리 할 수 있습니다. 클라이언트 별 구성 요소가 필요한 경우 사용자 지정을 사용하여 논리를 처리하고 클라이언트 코드에서 이에 대해 걱정하지 않습니다. 확실히 직접 만들 수 있습니다. 여기 에 IoC가 어떻게 도움이 될 수 있는지에 대한 예가 있습니다.
확장 성이 많은 지점에서 IoC를 사용하여 구성에서 구성 요소를로드 할 수 있습니다. 이것은 일반적인 빌드이지만 도구는 컨테이너에서 제공됩니다.
교차 절단 문제에 AOP를 사용하려는 경우 IoC는 메서드 호출을 가로채는 후크 를 제공합니다 . 이것은 일반적으로 프로젝트에서 임시로 수행되지 않지만 IoC를 사용하면 더 많은 것입니다.
이전에 이와 같은 기능을 작성했지만 지금은 기능이 필요하면 내 아키텍처에 맞으면 미리 빌드되고 테스트 된 도구를 사용합니다.
다른 사람들이 언급했듯이 사용하려는 클래스를 중앙에서 구성 할 수도 있습니다. 이것은 잘못된 방향과 복잡함을 힘써야합니다. 대부분의 응용 프로그램의 핵심 구성 요소는 많이 많지 않은 절충안을 만들기가 조금 더 어렵습니다.
IoC 컨테이너를 사용하고 기능에 감사하지만 절충점을 알아 차렸 음을 인정해야합니다. 내 코드는 클래스 수준에서는 더 명확하게 해지고 응용 프로그램 수준에서는 덜 명확하게 해 처리합니다 (즉, 제어 흐름 처리).
저는 회복중인 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 컨테이너를 사용하는 것의 가장 큰 이점은 (개인적으로 저는 Ninject를 사용합니다) 설정 및 다른 종류의 전역 상태 개체의 전달을 제거하는 것입니다.
저는 웹용으로 프로그래밍하지 않습니다. 저는 콘솔 응용 프로그램이며 트리의 깊숙한 곳에서 생성 된 개체 트리의 완전히 분리 된 분기에서 생성 된 사용자가 설정 또는 메타 데이터에 액세스 할 수 있습니다. IoC를 사용하면 Ninject에게 설정을 싱글 톤으로 처리하고 (항상 하나의 인스턴스 만 있기 때문에) 생성자에서 설정 또는 사전을 요청하고 presto ... 필요할 때 마술처럼 나타납니다!
IoC 컨테이너를 사용하지 않고 필요한 개체가 사용하기 전에 2, 3, ..., n 개체를 통해 설정 및 / 또는 메타 데이터를 전달해야합니다.
DI / IoC 컨테이너는 다른 많은 이점을 사용하는 사람들이 생성하는 아이디어에서 요청 요청 이동하는 것이 매우 유용하다는 것을 구부릴 수 있습니다. 있습니다. 당신의 무기고에!
IoC 프레임 워크는 다음과 같은 경우에 탁월합니다.
... 유형 안전을 버리십시오. 많은 (모두?) IoC 프레임 워크는 모든 것이 선택적으로 연결되어 선택 코드를 실행합니다. "이봐 요! 100 개 클래스의 초기화가 실패하지 않고, 널 포인터 예외가 발생하지 않습니다. 모든 설정이 완료 되었습니까?"
... 글로벌로 코드를 작성합니다 (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>();
}
}
또한 주문이 덜 분명합니다. 하스켈에서의 유형 Foo()
이 될 것입니다 IO Foo
하지만 당신은 정말 할 일 이없는 것입니다 IO
.
추상적 인 제거 비용 (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 프레임 워크를 사용하는 사람들이 매우 똑똑한 생각하지 않습니다. 저는 수학을 모른다고 믿습니다.
구현하는 프로그래머가 코드의 테스트 가능성, 유연성, 유지 관리 가능성 및 확장 성을 향상시키는 데 도움이되는 다양한 프로그래밍 방법을 사용하는 경향이 있음을 발견했습니다. 단일 책임 원칙, 우려 사항 분리 및 코딩과 같은 방법 API에 대해. 한 입 크기의 청크로 쉽게 읽을 수 있기 때문에 코드를 더 읽을 수 있습니다.
그러나 그것은 또한 다소 큰 의존성 트리를 생성하는 경향이 있는데, 이는 즉 손으로하는 것보다 프레임 워크 (즉, 규칙을 사용하는 경우)를 통해 훨씬 더 쉽게 관리 할 수 있습니다. 오늘 저는 LINQPad에서 정말 빠른 작업을 테스트하고 구현을 만들고 모듈에로드하는 것이 너무 번거로울 생각하고 결국 손으로 작성했습니다.
var merger = new SimpleWorkflowInstanceMerger(
new BitFactoryLog(typeof(SimpleWorkflowInstanceMerger).FullName),
new WorkflowAnswerRowUtil(
new WorkflowFieldAnswerEntMapper(),
new ActivityFormFieldDisplayInfoEntMapper(),
new FieldEntMapper()),
new AnswerRowMergeInfoRepository());
돌이켜 보면 IoC 프레임 워크를 사용하는 것이 더 빨 랐을 것입니다. 모듈이 모든 것을 관례에 따라 정의하기 때문입니다.
이 질문에 대한 답변과 의견을 연구하는 데 시간을 보냈기 때문에 IoC 컨테이너 사용에 반대하는 사람들이 진정한 의존성 배치를하고 있지 않다고 확신합니다. 내가 본 예는 일반적으로 도입과 혼동되는 사례입니다. 어떤 사람들은 코드를 "읽는"어려움에 대해 불평하고 있습니다. 그리고 수행 할 때 DI를 손으로 사용할 때 IoC 컨테이너를 사용할 때와 같이 대부분의 코드가 동일해야합니다. 차이점은 전적으로 응용 프로그램 내의 몇 가지 가지 "시작 지점"에 있어야합니다.
즉, IoC 컨테이너가 마음에 들지 않을 것입니다.
또 다른 요점 없습니다 : 리플렉션을 어디에서 사용한다면 사용할 수 있습니다. 리플렉션이 코드 탐색에 어떤 역할을하는지 싫어하지만 피할 수없는 특정 영역이 있어야합니다. 예를 들어 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 프레임 워크를 사용하여 다음 사항에 유의하십시오.
- 이 클래스를 단위 테스트 할 수 있습니다. 모의 생성하면을
ISimpleWorkflowInstanceMerger
데이터베이스 연결이나 기타 필요없이 예상대로 사용 되는지 테스트 할 수 있습니다. - 나는 상당한 규모의 코드를 사용하고 코드는 판독 할 수 있습니다.
- 내 소식의 중 하나가 변경하신 메시지가 없습니다. 여러 컨트롤러가 포함 된 중 일부를 사용할 가능성이 있고 점을 고려할 때 특히 유용합니다.
- 데이터 영역의 클래스를 명시 적으로 참조하지 않습니다. 내 웹 애플리케이션은
ISimpleWorkflowInstanceMerger
인터페이스를 포함 하는 프로젝트에 대한 참조 만 포함 할 수 있습니다 . 이를 통해 애플리케이션을 별도의 모듈로 나눌 수 있고 진정한 다중 계층 아키텍처를 구축 할 수 있으므로 작업이 훨씬 더 유연 해집니다.
일반적인 웹 애플리케이션에는 꽤 많은 컨트롤러가 있습니다. 각 컨트롤러에서 직접 DI를 수행하는 모든 고통은 애플리케이션이 성장에 따라 실제로 더해 처리합니다. 리플렉션을 통해 서비스를 인스턴스화하지 않는 많은 루트가 하나 뿐인 애플리케이션이있는 큰 문제는 아닙니다. 그럼에도 불구하고 그래프를 관리하기 위해 어떤 종류의 프레임 워크를 사용하지 않는 어떤 종류의 프레임 워크를 사용하는 모든 응용 프로그램은 특정 크기에 도달하면 관리하는 데 매우 많은 비용이 듭니다.
"new"키워드를 사용할 때마다 구체적인 클래스가 머 생성되고 약간의 알람 벨이 릿 속에 울려 야합니다. 이 개체를 격리하여 테스트하는 것이 더 어려워집니다. 솔루션은 인터페이스를 프로그래밍하고 구현하는 인터페이스를 구현하는 모든 개체 (예 : 모의)로 개체를 단위 테스트 할 수 있습니다.
문제는 어딘가에 물건을 충전하는 것입니다. 팩토리 패턴은 POXO에서 커플 링을 전환하는 한 가지 방법입니다 (일반 이전 "여기에 OO 언어 삽입"개체). 여러분과 여러분의 동료가 모두 이와 같은 코드를 작성하고 권한 IoC 컨테이너는 여러분의 코드베이스에 만들 수있는 다음 "점진적 개선"입니다. 그것은 당신의 불만 한 공장 상용구 코드를 옮길 것입니다. 그것을 그것을 그것을 그것을 사랑할 것입니다. 도대체 왜 당신이 그것을 좋아하는지에 대해 회사에 이야기하고의 열광을 얻습니다.
동료가 아직 DI를 수행하고 있지 않다면 먼저 집중하는 것이 좋습니다. 쉽게 테스트 할 수있는 육종 코드를 작성하는 방법에 대해 알리십시오. DI 코드는 어려운 부분입니다. 일단 거기에 있어야만 배선 논리를 Factory 클래스에서 IoC 컨테이너로 이동하는 것이 간단해야합니다.
모든 것이 명확하게 표시되기 때문에 느슨하게 결합되고 애플리케이션 전체에서 쉽게 액세스하고 보완 할 수있는 구성 요소를 만들 수 있습니다.
IoC 컨테이너 가 필요하지 않습니다 .
그러나 DI 패턴을 엄격하게 준수하고있는 것이 사용하면 많은 것이 있고 지루한 코드가 제거 될 것입니다.
어쨌든 라이브러리 / 프레임 워크를 사용하는 가장 좋은시기는 라이브러리가 무엇을하고 있는지 이해하고 라이브러리 없이도 할 수있는 경우입니다.
나는 가정에서 재배 한 DI 코드를 꺼내 IOC로 대체하는 과정에 있습니다. 아마도 200 줄이 코드를 제거하고 약 10 줄로 대체했을 것입니다. 예, 컨테이너 (Winsor) 사용 방법에 약간의 배워야했지만 인터넷 기술을 연구하는 엔지니어입니다. 21 세기에 익숙해졌습니다. 나는 아마 약 20 분 동안 방법을 체험했습니다. 이 내 시간의 가치가 충분했습니다.
계속해서 클래스를 분리하고 반전을 반전하고 클래스 클래스는 계속 작게 유지되고 "종속성 그래프"의 크기는 계속 커집니다. (즉 나쁘지.) IoC 컨테이너의 기본 기능을 사용하면 모든 것을 수행 할 수 있습니다. 예를 들어, "Foo"의 새 인스턴스를 만들고 싶지만 "Bar"가 필요한 경우 어떻게해야합니까? 그리고 "Bar"에는 "A", "B"및 "C"가 필요합니다. 그리고 추가는 3 개의 다른 것 등이 필요합니다. (예, 좋은 가짜 이름을 만들 수 없습니다. :)).
IoC 컨테이너를 사용하여 개체 그래프를 빌드하면 구성이 크게 고 일회성 구성으로 푸시됩니다. 나는 "나에게 'Foo'를 만들어 줘"라고 말하면 그것을 만드는 데 필요한 것이 무엇인지 알아 낸다.
어떤 사람들은 훨씬 더 많은 인프라에 IoC 컨테이너를 사용하는데, 이는 고급 시나리오에 적합하지만이 새로운 개발자를 코드를 난독 화하고 읽기 및 디버그하기 어렵게 만들 수있는 경우 동의합니다.
Unity에 대한 이야기. 너무 커지면 서까래에서 삐걱 거리는 소리가 들립니다.
사람들이 C ++의 템플릿이 90 년으로 돌아가는 우아한 방법 인 방법에 대해 한때 이야기했던 것과 같은 종류의 사람들과 같은 종류의 사람들과 같은 종류의 사람들과 동일한 IoC 코드가 지에 대해 사람들이 말을 시작했을 때 때 놀라지 않습니다. . 바!
.NET 세계에서 AOP는 그다지 대중적이지 않습니다. DI의 경우 프레임 워크는 직접 작성하든 다른 프레임 워크를 사용하든 유일한 선택입니다.
AOP를 사용하는 경우 애플리케이션을 할 때 삽입 할 수 있으며 Java에서 더 일반적입니다.
결합 감소와 같은 DI에는 많은 이점이 있으므로 단위 테스트가 더 지지만 어떻게 구현할까요? 반사를 사용하여 직접 수행 하시겠습니까?
그래서, 거의 3 년이 됐죠?
IoC 프레임 워크에 찬성하는 응답자의 50 %는 IoC와 IoC 프레임 워크의 차이를 이해하지 못합니다. 앱 서버에 배포하지 코드를 사용하는 것을 알지 못합니다.
가장 널리 사용되는 Java Spring 프레임 워크를 사용하면 XMl에서 코드로 이동 한 IoC 구성이며 이제 다음과 가변됩니다.
`@Configuration 공개 클래스 AppConfig {
public @Bean TransferService transferService() {
return new TransferServiceImpl(accountRepository());
}
public @Bean AccountRepository accountRepository() {
return new InMemoryAccountRepository();
}
}`이 작업을 수행하기위한 프레임 워크가 필요합니다. 왜 정확히?
솔직히 IoC 컨테이너가 필요한 경우가 많지 많지 대부분의 경우에는해야 할 일을 추가합니다.
객체의 구성을 더 간단하게 만들기 위해 사용하는 경우,이를 둘 이상의 위치에서 인스턴스화하고 있습니까? 싱글 톤이 당신의 필요에 맞지 보장 혜택이 있습니까? 운영에 구성을 변경하고 있습니까? (데이터 소스 유형 전환 등).
IoC 컨테이너가 필요합니다. 존재하는 경우 개발자가 쉽게 볼 수있는 곳에서 초기화를 이동하는 것입니다.
어쨌든 인터페이스가 상속보다 낫다고 누가 말했습니까? 서비스를 테스트하고 가정 해 보겠습니다. 생성자 DI를 사용하지 않고 상속을 사용하여 모의를 만드는 이유는 무엇입니까? 내가 사용하는 대부분의 서비스에는 몇 가지 사용하는 것이 있습니다. 방식으로 단위 이러한 테스트를 수행하면 쓸모없는 인터페이스가 많이 유지되는 것을 방지 할 수 있으며 ReSharper를 사용하여 메서드를 선언을 빠르게 찾을 필요가 없습니다 .
대부분의 구현에서 IoC 컨테이너가 불필요한 코드를 제거하는 것은 신화라고 생각합니다.
먼저 컨테이너를 설정해야합니다. 그런 다음 초기화해야하는 각 개체를 정의해야합니다. (객체가 두 번 이상 사용되지 않는 한. Singleton으로 더 나은가요?). 그런 다음 초기화 방식 초기화 한 각 개체에 대해 인터페이스를 만들고 유지 관리해야합니다.
누구든지 이것에 대해 생각이 있습니까?
대량으로 쉽게 교체 할 수있는 경우 구성을 중앙 집중화하는 경우 IoC 컨테이너가 필요합니다. TDD에서 상호 사용이 가능합니다. 이것은 어느 정도 난독 화하는 대가로 수행되는 구성 구성의 제어 흐름을 수행하고 체계적으로 구성되어 있어야하는 것이 중요합니다. 이렇게 할 이유가있는 것도 좋은데, 복잡한 추상화 금도금 입니다. 나는 그것이 생성자에있는 것으로, 문에 해당하는 것을 끌어 내려 졌을 제대로 수행되지 않은 것을 보았습니다.
그 이유는 다음과 가변적 입니다. 이 프로젝트는 IOC-with-Ninject라고합니다. Visual Studio로 설치하여 언어 수 있습니다. 이 예제는 Ninject를 사용하지만 모든 'new'문은 한 위치에 있고 사용할 수있는 바인드 모듈을 변경하여 애플리케이션 실행 방식을 적용 할 수 있습니다. 이 예제는 서비스의 모의 또는 실제 버전에 바인딩 할 수 버전 설정되었습니다. 중요하지 않은 소규모 프로젝트에서는 큰 문제가 발생합니다.
명확하게 말하면, 이점은 다음과 달라집니다. 1) 코드 루트의 한 위치에 모든 새로운 명령문이 있습니다. 2) 한 번의 변경으로 코드를 완전히 리팩터링합니다. 3) '쿨 팩터'에 대한 추가 점수 '이기 때문에 ... 음 : 멋지다. : 피
나는 왜 IOC가 내 관점에서 볼려고 노력할 것입니다.
다른 모든라고 요구되는 IOC 컨테이너 (또는 아인슈타인이 I = OC ^ 2했듯이)는 스스로 결정해야하는 개념입니다. 최근 IOC에 대한 패션 외침은 패션뿐입니다. 패션에 빠지지 코드에 구현할 수있는 수많은 개념이 있습니다. 우선 프로그래밍을 사용하고 그 이름을 대중화하기 위해 사용하고 자체적으로 배웠습니다. 의존성 제어는 매우 오래된 주제이며 무엇과 분리 된 방식으로 해결됩니다. 모든 것에서 모든 것을 분리하는 것. IOC 컨테이너의 문제점은 Entity Framework 또는 NHibernate만큼 유용하다는 것입니다. 즉각적인 필수 사항이지만 IOC 컨테이너는 항상 필요한 것은 아닙니다. IOC 컨테이너가 유용 할 때 :
- 많은 대화가있는 상황이있을 때 구성하고 싶은
- 코드를 보관 제품과 결합하는 데 신경 쓰지 않는 경우
- 개발자가 새로운 도구로 작업하는 방법을 배우고 싶을 때
1 : 코드에 너무 많은 경우이 디자인 초기에 사용하던 경우 그리 자주 발생하지 않습니다. 일상적인 사고는 사고가 필요할 때 유용합니다.
2 : 코드를 안전하게 코드와 결합하는 것은 HuGe 문제입니다. 나는 10 년 이상 된 코드로 작업하고 COM, 거기에 ATL, COM + 등의 고급스럽고 고급 개념을 고고하고 있습니다. 지금은 그 코드로 할 수있는 일이 없습니다. 내가 말하는 것은 고급 개념이 명백한 이점을 제공하지만 오래된 이점 자체로 인해 장기적으로 취소 할 것입니다. 그것은 모든 것을 더 비싸게 만들었습니다.
3 : 소프트웨어 개발은 충분히 어렵습니다. 고급 개념을 코드에 적용 할 수 있도록 허용하면 인식 할 수없는 수준으로 확장 할 수 있습니다. IOC2에 문제가 있습니다. 분리를 분리하지만 논리 흐름도 분리합니다. 버그를 발견하고 상황을 조사하기 위해 버그를 발견하고 취해야한다고 상상해. IOC2는 다른 고급 개념과 추가를 더욱 어렵게 만듭니다. 개념 내에서 버그를 수정하는 것은 평범한 코드에서 버그를 수정하는 것보다 더 어렵습니다. 버그를 수정하면 개념을 다시 준수해야하기 때문입니다. (예를 들어, C ++ .NET은 예약으로 구문을 너무 많이 변경을 .NET의 이전 버전 리팩토링하기 전에 신중하게 생각해야합니다.) IOC의 문제는 무엇입니까? 문제는 해결에 있습니다. 해결 방법은 일반적으로 IOC2 자체에 숨겨져 있습니다. 배우고 유지해야하는 드문 방법으로 작성해야합니다. 제 3 자 제품이 5 년 안에 출시 될 예정입니까? Microsoft는 존재합니다.
IOC2와 관련하여 "우리는 방법을 알고 있습니다". 이것은 자동화 테스트와 유사합니다. 멋진 용어와 완벽한 솔루션을 한눈에보기 만하면 모든 테스트를 밤새 실행하고 아침에 결과를 볼 수 있습니다. 자동화 된 테스트가 실제로 무엇을 의미하는지 회사별로 설명하는 것은 정말 고통 스럽습니다. 자동화 된 테스트는 제품의 품질을 높이기 위해 하룻밤 사이에 도입 할 수있는 버그 수를 줄이는 방법이 아닙니다. 그러나 패션은 그 개념을 성가 시게 지배적으로 만들고 있습니다. IOC2는 같은 증후군을 앓고 있습니다. 당신의 소프트웨어가 좋으려면 그것을 구현해야한다고 믿어집니다. EvErY 최근 인터뷰 IOC2 및 자동화를 구현하고 물었습니다. 그것은 패션의 징조입니다. 회사는 MFC로 코드의 일부를 포기하지 않을 것입니다.
소프트웨어의 다른 개념으로 IOC2를 배워야합니다. IOC2 사용 여부는 팀과 회사 내에서 결정합니다. 그러나 모든 결정을 내리기 전에 위의 주장을 언급해야합니다. 플러스 쪽이 마이너스 쪽보다 크다는 것을 알게 된 경우에만 긍정적 인 결정을 내릴 수 있습니다.
IOC2가 해결 문제 만 해결하고 도입 한 문제를 소개한다는 점을 제외하면 IOC2에는 많은 문제가 없습니다. 다른 건 없습니다. 그러나 패션에 반대하는 것은 매우 어렵고, 입에 땀을 흘리며 모든 것을 추종합니다. 결혼식 공상의 문제가 명백해 졌을 때 그들 중 누구도 거기에 있고 이상합니다. 소프트웨어 산업의 많은 개념은 이익을 창출하고, 책을 쓰고, 컨퍼런스를 개최하고, 신제품을 만들었습니다. 그것은 일반적으로 짧은 수명의 패션입니다. 사람들은 다른 것을 발견하자마자 완전히 버립니다. IOC2는 유용하지만 내가 본 다른 많은 사라진 개념과 회의 시청. 나는 그것이 살아남을지 모른다. 대한 규칙은 없습니다. 유용하다면 살아남을 생각합니다. 아니요, 그렇게되지 않습니다. 하나의 큰 부유 한 회사만으로 충분하며 개념은 몇 주 안에 사라질 수 있습니다. 우리는 볼 것이다. NHibernate는 살아 남았고 EF는 2 위를 차지했습니다. 아마도 IOC2도 살아남을 것입니다. 소프트웨어 개발의 대부분의 개념은 특별한 것이 매우 안전하고 단순하며 분명하며 현재 명명 규칙을 기억하는 것이 더 어렵다는 것입니다.IOC2에 대한 지식이 개발자를 더 나은 개발자로 만드는가? 아니요, 개발자가 기본적으로 IOC2와 동일한 개념을 생각할 수있는 모든 IOC2가 어떤 문제를 해결하고 있는지 이해하기 위해 사용할 것입니다. 그것은 바로 옳기 때문입니다. 소프트웨어 개발의 대부분의 개념은 특별한 것이 매우 안전하고 단순하며 분명하며 현재 명명 규칙을 기억하는 것이 더 어렵다는 것입니다. IOC2에 대한 지식이 개발자를 더 나은 개발자로 만드는가? 아니요, 개발자가 기본적으로 IOC2와 동일한 개념을 생각할 수있는 모든 IOC2가 어떤 문제를 해결하고 있는지 이해하기 위해 사용할 것입니다. 그것은 바로 옳기 때문입니다. 소프트웨어 개발의 대부분의 개념은 특별한 것이 매우 안전하고 단순하며 분명하며 현재 명명 규칙을 기억하는 것이 더 어렵다는 것입니다. IOC2에 대한 지식이 개발자를 더 나은 개발자로 만드는가? 아니요, 개발자가 기본적으로 IOC2와 동일한 개념을 생각할 수있는 모든 IOC2가 어떤 문제를 해결하고 있는지 이해하기 위해 사용할 것입니다. 그것은 바로 옳기 때문입니다. IOC2에 대한 지식이 개발자를 더 나은 개발자로 만드는가?아니요, 개발자가 기본적으로 IOC2와 동일한 개념을 생각할 수있는 모든 IOC2가 어떤 문제를 해결하고 있는지 이해하기 위해 사용할 것입니다. 그것은 바로 옳기 때문입니다. IOC2에 대한 지식이 개발자를 더 나은 개발자로 만드는가? 아니요, 개발자가 기본적으로 IOC2와 동일한 개념을 생각할 수있는 모든 IOC2가 어떤 문제를 해결하고 있는지 이해하기 위해 사용할 것입니다. 그것은 바로 옳기 때문입니다.
개인적으로 IoC를 내 응용 프로그램의 맵으로 사용합니다 (예, StructureMap도 선호합니다;)). 테스트 중에 일반적인 인터페이스 구현을 Moq 구현으로 쉽게 대체 할 수 있습니다. 테스트 설정을 만드는 것은 내 IoC 프레임 워크에 대한 새로운 init-call을 만드는 큼 쉬울 수 있으며 테스트가되는 클래스를 모의로 대체합니다.
이것은 아마도 IoC가있는 것이 아니라 가장 많이 사용하는 것입니다 ..
IOC 컨테이너는 사용자가 해결하는 문제입니다.
의존성 필요를 위해 프레임 워크가 필요하지 않습니다. 핵심 Java 개념으로도이를 수행 할 수 있습니다. http://en.wikipedia.org/wiki/Dependency_injection#Code_illustration_using_Java
나는 다소 오래된 게시물이라는 것을 알고 있다고 여전히 합리적으로 활성 한 많은 보이며 다른 답변에서 아직없는 것들 몇 가지 포인트를 기여할 생각했습니다.
나는 의존성 제공의 이점에 동의 할 것입니다. 나는 Maxm007의 대답에서 서로 다르지 않은 패턴을 사용하여 직접 구성하고 선호합니다. 보관 컨테이너를 사용할 때 두 가지 주요 문제를 발견했습니다.
1) 수명을 "자동"으로 관리하면 예상치 못한 결과를 얻을 수 있습니다. 특히 훨씬 많은 경우에 훨씬 더 많은 경우에 훨씬 더 많은 메모리를 관리 할 수 있습니다. 사용하는 프레임 워크에 따라 다를 수 있음에도 불구하고 문제는 존재합니다. 개체에 리소스, 데이터 연결 등이있는 경우에도 개체가 예상보다 오래 지속될 수 있으므로 문제가 발생할 수 있습니다. 따라서 IoC 컨테이너는 애플리케이션의 리소스 활용도와 메모리 공간을 증가시키는 경향이 있습니다.
2) IoC 컨테이너는 "블랙 박스 프로그래밍"의 한 형태입니다. 특히 경험이 부족한 개발자가이를 악용하는 경향이 있음을 발견했습니다. 이를 통해 프로그래머는 상호 작용할 필요가 없습니다. 왜냐하면 허공에서 원하는 객체를 간단하게 제공하기 때문입니다. 예를 들어, ObjectA가 ObjectB에 대해 직접 알면 안되는 좋은 디자인 이유가있을 수 있습니다, 경험이없는 프로그래머는 "없음, IoC 컨테이너에서 ObjectB를 가져옵니다."라고 말할 수 있습니다. ". 이는 IoC가 방지하는 데 도움이되는 결합을 증가시킬 수 있습니다.
ASP.NET 프로젝트에서 몇 줄의 코드로 수행 할 수 있습니다. 여러 가지 엔드를 사용하고 단위 테스트가 필요한 앱이 몇 가지있을 때 컨테이너를 사용하는 것이 많은 이점이 있다고 생각합니다.
'ProgramingTip' 카테고리의 다른 글
Android 개발 도구 v. 23으로 Eclipse 업데이트 (0) | 2020.10.03 |
---|---|
.then () 체인에서 이전 약속 결과에 어떻게 액세스 할 수 있습니까? (0) | 2020.10.03 |
grep은 검색 할 수있는 패턴과 일치하는 단어 만 표시 할 수 있습니까? (0) | 2020.10.03 |
MySQL 쿼리 GROUP BY 일 / 월 / 년 (0) | 2020.10.03 |
SVN으로 디렉토리를 선택하고 있습니까? (0) | 2020.09.29 |