누군가 나에게 다가 가서 개념적 방식으로 Dependency Injection을 정의하고 소프트웨어 디자인에서 DI를 사용하는 실제 장단점을 설명하도록 요청할 때마다. 나는 DI의 개념을 설명하기가 어렵다고 고백합니다. 나는 그들에게 단일 책임 원칙, 상속에 대한 구성 등에 관한 역사를 말할 필요가있을 때마다.
누구나 개발자를 위해 DI를 설명하는 가장 좋은 방법을 설명하는 데 도움을 줄 수 있습니까?
누군가 나에게 다가 가서 개념적 방식으로 Dependency Injection을 정의하고 소프트웨어 디자인에서 DI를 사용하는 실제 장단점을 설명하도록 요청할 때마다. 나는 DI의 개념을 설명하기가 어렵다고 고백합니다. 나는 그들에게 단일 책임 원칙, 상속에 대한 구성 등에 관한 역사를 말할 필요가있을 때마다.
누구나 개발자를 위해 DI를 설명하는 가장 좋은 방법을 설명하는 데 도움을 줄 수 있습니까?
답변:
의존성 주입 은 다소 간단한 개념에 대한 끔찍한 이름 (IMO) 1 입니다. 예를 들면 다음과 같습니다.
DbContext
)를 생성하고 관리합니다 . 이 내부 리소스는 종속성 이라고합니다.DbContext
메소드에서 자원의 작성 및 관리 (예 :)를 제거 하고이 메소드를 자원을 제공하는 호출자의 책임 (메소드 매개 변수 또는 클래스의 인스턴스화)
[1] : 저수준의 배경에서 왔으며, 의존성 주입을 배우고 배우는 데 몇 달이 걸렸습니다. 이름은 DLL 주입 과 같이 훨씬 더 복잡 할 수 있기 때문 입니다. Visual Studio (및 일반적으로 개발자)는 종속성 이 전혀 도움이되지 않으므로 프로젝트가 의존 하는 .NET 라이브러리 (DLL 또는 어셈블리 )를 참조 합니다. Dependency Walker (depends.exe) 와 같은 것도 있습니다 .
[편집] 일부 데모 코드가 일부에는 유용 할 것이라고 생각했습니다. 여기에 C #이 있습니다.
의존성 주입이없는 경우 :
public class Repository : IDisposable
{
protected DbContext Context { get; }
public Repository()
{
Context = new DbContext("name=MyEntities");
}
public void Dispose()
{
Context.Dispose();
}
}
그런 다음 소비자는 다음과 같은 작업을 수행합니다.
using ( var repository = new Repository() )
{
// work
}
의존성 주입 패턴으로 구현 된 동일한 클래스는 다음과 같습니다.
public class RepositoryWithDI
{
protected DbContext Context { get; }
public RepositoryWithDI(DbContext context)
{
Context = context;
}
}
이제 인스턴스화하고 클래스에 DbContext
전달 (errm, inject )하는 것은 호출자의 책임입니다 .
using ( var context = new DbContext("name=MyEntities") )
{
var repository = new RepositoryWithDI(context);
// work
}
추상적 개념은 종종 실제 세계 비유를 사용하여 더 잘 설명됩니다. 이것은 나의 비유입니다.
샌드위치 가게를 운영합니다. 당신은 놀라운 샌드위치를 만들지 만 빵 자체에 대해서는 거의 알지 못합니다. 하얀 빵만 먹을 수 있습니다. 빵을 샌드위치로 만드는 데 사용하는 토핑에 전적으로 집중합니다.
그러나 일부 고객은 실제로 갈색 빵을 선호합니다. 일부는 통 곡물을 선호합니다. 어느 쪽이든 상관하지 않습니다. 비슷한 크기의 빵이라면 놀라운 샌드위치를 만들 수 있습니다. 또한 여러 종류의 빵을 조달하고 재고를 유지해야하는 추가 책임을 감수하고 싶지도 않습니다. 여러 종류의 빵을 비축하더라도 합리적인 예측을 할 수없는 빵에 이국적인 맛이있는 고객이 항상있을 것입니다.
따라서 고객은 자신의 빵을 가져 오는 새로운 규칙을 제정합니다. 더 이상 빵을 직접 제공하지 않습니다. 이것은 상생의 상황입니다. 고객은 원하는 정확한 빵을 얻을 수 있으며, 더 이상 관심없는 빵을 구할 필요가 없습니다. 결국, 당신은 제빵사가 아닌 샌드위치 메이커입니다.
아, 그리고 자신의 빵을 사고 싶지 않은 고객을 수용하기 위해, 당신은 옆에 원래의 부드러운 흰 빵을 판매하는 두 번째 상점을 열었습니다. 자신의 빵을 가져 오지 않은 고객은 기본 빵을 가져와 샌드위치를 만들기 위해 당신에게 와야합니다.
완벽하지는 않지만 주요 기능인 소비자에게 제어 기능을 강조합니다 . 본질적인 윈-윈은 더 이상 자신의 의존성을 얻을 필요가없고, 소비자는 자신의 의존성을 선택하는 데 방해가되지 않는다는 것입니다.
그것에 대한 간단한 답변 :
무엇보다도 클래스는 잘 정의 된 책임을 가져야하며이 범위 밖의 모든 항목은 해당 클래스 외부에 유지해야합니다. 이렇게 말하면 Dependency Injection은 "제 3 자"의 도움을 받아 다른 클래스 B의 기능을 클래스 A에 주입하여 이러한 분리 문제를 해결함으로써 클래스 A가 범위를 벗어난 일부 작업을 완료하도록 도와줍니다.
.Net Core는이 프레임 워크가 많은 의존성 주입을 사용하기 때문에 제공 할 수있는 좋은 예입니다. 일반적으로 삽입하려는 서비스는 startup.cs
파일에 있습니다.
물론, 학생은 다형성, 인터페이스 및 OOP 디자인 원칙과 같은 일부 개념을 알고 있어야합니다.
본질적으로 간단한 개념은 많은 보풀과 분쿰이 있습니다.
코드에서 간단하게 수행 할 수있을 때 " 어떤 프레임 워크를 사용해야 합니까?"라는 문제가 발생 하기 쉽습니다 .
이것은 내가 개인적으로 사용하는 정의입니다.
의존성 주입은 Y가없는 경우에도 Y의 인스턴스에 대한 기준을 만족시키는 모든 Y를 제공하는 기능을 포함합니다.
Y가 파일 시스템 또는 데이터베이스 연결 인 경우가 있습니다.
moq 와 같은 프레임 워크 는 인터페이스를 사용하여 이중 (Y의 척 버전)을 정의 할 수 있으므로 Y의 인스턴스에 삽입 할 수 있습니다. 여기서 Y는 데이터베이스 연결입니다.
이것이 순전히 단위 테스트 문제라고 생각하는 함정에 빠지기 쉽지만 변경이 예상되고 논쟁의 여지가있는 코드의 모든 비트에 매우 유용한 패턴입니다.
이 작업을 올바르게 수행하려면 먼저 종속성 및 주입을 정의해야합니다.
기본적인 예는 두 개의 값을 더하는 방법입니다. 분명히이 방법은 값을 추가해야합니다. 그것들을 인수로 전달하여 제공된다면, 이것은 이미 의존성 주입의 경우입니다. 대안은 피연산자를 속성 또는 전역 변수로 구현하는 것입니다. 이렇게하면 종속성이 주입되지 않고 외부에서 사전에 사용 가능합니다.
대신 속성을 사용하고 이름을 A와 B로 지정한다고 가정합니다. 이름을 Op1 및 Op2로 변경하면 Add 메서드가 중단됩니다. 또는 IDE가 모든 이름을 업데이트합니다. 요점은 메서드가 외부 리소스에 종속되어 있기 때문에 업데이트해야 할 시점입니다.
이 예제는 기본이지만 메소드가 이미지와 같은 객체에 대해 작업을 수행하거나 파일 스트림에서 읽는 더 복잡한 예제를 상상할 수 있습니다. 이미지의 위치를 알아야하는 방법으로 이미지에 도달하기를 원하십니까? 아니요. 파일 자체를 여는 방법을 원하여 파일을 어디에서 찾아야하는지 또는 파일에서 읽을 것인지 알아야합니까? 아니.
요점 : 분석법의 기능을 핵심 행동으로 줄이고 환경과 분석법을 분리하는 것. 두 번째를 수행하여 첫 번째를 얻습니다.이를 종속성 주입의 정의로 간주 할 수 있습니다.
장점 : 분석법 환경에 대한 종속성이 제거되었으므로 분석법 변경은 환경에 영향을 미치지 않으며 그 반대도 마찬가지입니다. => 응용 프로그램을 유지 관리하기가 더 쉬워집니다 (수정).