문제

에 대한 구체적인 질문과 함께 이미 여러 가지 질문이 게시되었습니다. 의존성 주입, 언제 사용해야 하는지, 어떤 프레임워크가 있는지 등을 설명합니다.하지만,

종속성 주입이란 무엇이며 언제/왜 사용하거나 사용해서는 안 됩니까?

도움이 되었습니까?

해결책

의존성 주입 다른 사람에게 의존성을 전달하고 있습니다 사물 또는 뼈대(종속성 인젝터).

종속성 주입으로 테스트가 더 쉬워집니다. 주입은이를 통해 수행 할 수 있습니다 건설자.

SomeClass() 생성자가 다음과 같이 있습니다.

public SomeClass() {
    myObject = Factory.getObject();
}

문제: 만약에 myObject 디스크 액세스 또는 네트워크 액세스와 같은 복잡한 작업이 포함됩니다. 딱딱한 단위 테스트를 수행합니다 SomeClass(). 프로그래머는 조롱해야합니다 myObject 그리고 아마도 인터셉트 공장 전화.

대체 솔루션:

  • 통과 myObject 생성자에 대한 논쟁으로
public SomeClass (MyClass myObject) {
    this.myObject = myObject;
}

myObject 테스트를 더 쉽게 할 수 있도록 직접 전달할 수 있습니다.

  • 일반적인 대안 중 하나는 a를 정의하는 것입니다 하지 않는 생성자. 종속성 주입은 세터를 통해 수행 할 수 있습니다. (h/t @mikevella).
  • 마틴 파울러 세 번째 대안 (h/t @marcdix)을 문서화합니다. 클래스는 인터페이스를 명시 적으로 구현합니다 종속성의 경우 프로그래머가 주입되기를 바랍니다.

의존성 주입없이 단위 테스트에서 구성 요소를 분리하기가 더 어렵습니다.

2013 년 에이 답변을 썼을 때 이것은 주요 주제였습니다. Google 테스트 블로그. 프로그래머가 런타임 설계 (예 : 서비스 로케이터 또는 유사한 패턴의 경우)에서 항상 추가 유연성이 필요하지는 않기 때문에 가장 큰 이점으로 남아 있습니다. 프로그래머는 종종 테스트 중에 클래스를 분리해야합니다.

다른 팁

내가 지금까지 찾은 최고의 정의는입니다 제임스 쇼어에 의해 하나:

"의존성 주입"은 5 센트 개념의 25 달러짜리 용어입니다. [...] 종속성 주입은 객체에 인스턴스 변수를 제공하는 것을 의미합니다. [...].

거기 있습니다 Martin Fowler의 기사 그것은 또한 유용 할 수 있습니다.

종속성 주입은 기본적으로 객체가 자체적으로 구성하는 대신 객체가 필요한 객체 (그 종속성)를 제공합니다. 종속성을 조롱하거나 스터브 아웃 할 수 있기 때문에 테스트에 매우 유용한 기술입니다.

종속성은 여러 가지 방법으로 물체에 주입 될 수 있습니다 (예 : 생성자 주입 또는 세터 주입). 특수 의존성 주입 프레임 워크 (예 : 스프링)를 사용하여 그렇게 할 수도 있지만 확실히 필요하지 않습니다. 의존성 주입을 위해 해당 프레임 워크가 필요하지 않습니다. 객체 (종속성) 인스턴스화 및 통과 (종속성)는 프레임 워크에 의한 주입만큼이나 좋은 주입입니다.

나는이 재미있는 예를 찾았다 느슨한 결합:

모든 응용 프로그램은 유용한 내용을 수행하기 위해 서로 협력하는 많은 객체로 구성됩니다. 전통적으로 각 객체는 공동 작업하는 종속 객체 (종속성)에 대한 자체 참조를 얻을 책임이 있습니다. 이는 고도로 결합 된 클래스와 테스트 어려운 코드로 이어집니다.

예를 들어, 고려하십시오 Car 물체.

Car 바퀴, 엔진, 연료, 배터리 등에 달려 있습니다. 전통적으로 우리는 그러한 의존 대상의 브랜드를 정의와 함께 정의합니다. Car 물체.

의존성 주입없이 (DI) :

class Car{
  private Wheel wh = new NepaliRubberWheel();
  private Battery bt = new ExcideBattery();

  //The rest
}

여기, Car 물체 종속 객체를 작성하는 책임이 있습니다.

종속 객체의 유형을 변경하려면 어떻게해야합니까? Wheel - 초기 후 NepaliRubberWheel() 구멍? 새로운 의존성으로 자동차 객체를 재현해야합니다. ChineseRubberWheel(), 그러나 단지 Car 제조업체는 그렇게 할 수 있습니다.

그러면 무엇을합니까 Dependency Injection 우리를 위해 ...?

종속성 주입을 사용할 때는 객체에 종속성이 부여됩니다. 컴파일 시간이 아닌 런타임 (자동차 제조 시간). 이제 우리가 변경할 수 있도록 Wheel 우리가 원할 때마다. 여기, dependency (wheel) 주사 할 수 있습니다 Car 런타임에.

종속성 주입 후 :

여기, 우리는 있습니다 주사 그만큼 의존성 런타임에 (휠 및 배터리). 따라서 용어 : 의존성 주입.

class Car{
  private Wheel wh = // Inject an Instance of Wheel (dependency of car) at runtime
  private Battery bt = // Inject an Instance of Battery (dependency of car) at runtime
  Car(Wheel wh,Battery bt) {
      this.wh = wh;
      this.bt = bt;
  }
  //Or we can have setters
  void setWheel(Wheel wh) {
      this.wh = wh;
  }
}

원천: 의존성 주입 이해

종속성 주입은 객체가 내부적으로 구성하는 대신 다른 코드에서 객체의 인스턴스를받는 방식으로 객체를 설계하는 관행입니다. 즉, 객체에 필요한 인터페이스를 구현하는 모든 객체는 코드를 변경하지 않고도 대체 할 수 있으며, 이는 테스트를 단순화하고 디커플링을 향상시킵니다.

예를 들어,이 클라스를 고려하십시오.

public class PersonService {
  public void addManager( Person employee, Person newManager ) { ... }
  public void removeManager( Person employee, Person oldManager ) { ... }
  public Group getGroupByManager( Person manager ) { ... }
}

public class GroupMembershipService() {
  public void addPersonToGroup( Person person, Group group ) { ... }
  public void removePersonFromGroup( Person person, Group group ) { ... }
} 

이 예에서는의 구현입니다 PersonService::addManager 그리고 PersonService::removeManager 인스턴스가 필요합니다 GroupMembershipService 일을하기 위해. 의존성 주입 없이는이 작업을 수행하는 전통적인 방법은 새로운 것을 인스턴스화하는 것입니다. GroupMembershipService 생성자에서 PersonService 그리고 해당 인스턴스 속성을 두 함수 모두에서 사용하십시오. 그러나 생성자 인 경우 GroupMembershipService 필요한 여러 가지가 있거나 더 나쁘지만, 초기화 "세터"가 있습니다. GroupMembershipService, 코드는 다소 빨리 커집니다 PersonService 이제는에 달려 있습니다 GroupMembershipService 그러나 다른 모든 것 GroupMembershipService 에 따라 다릅니다. 또한, 연결 GroupMembershipService 하드 코딩된다 PersonService 이는 당신이 "더미"를 "더미"할 수 없다는 것을 의미합니다. GroupMembershipService 테스트 목적으로 또는 응용 프로그램의 다른 부분에서 전략 패턴을 사용합니다.

의존성 주입으로, 인스턴스화하는 대신 GroupMembershipService 당신 안에 PersonService, 당신은 그것을 전달할 것입니다 PersonService 생성자 또는 그렇지 않으면 속성 (getter and setter)을 추가하여 로컬 인스턴스를 설정합니다. 이것은 당신을 의미합니다 PersonService 더 이상 GroupMembershipService, 그것은 단지 그것이 주어진 것을 받아들이고 그들과 함께 일합니다. 이것은 또한 서브 클래스의 모든 것이 GroupMembershipService, 또는 구현 GroupMembershipService 인터페이스는 "주입"할 수 있습니다 PersonService, 그리고 PersonService 변화에 대해 알 필요가 없습니다.

허용 된 대답은 좋은 대답입니다. 그러나 DI는 코드에서 하드 코드 상수를 피하는 고전적인 것과 매우 흡사하다는 것을 추가하고 싶습니다.

데이터베이스 이름과 같은 상수를 사용하면 코드 내부에서 일부 구성 파일로 빠르게 이동하고 해당 값이 포함 된 변수를 필요한 위치로 전달합니다. 그렇게해야 할 이유는 이러한 상수가 일반적으로 나머지 코드보다 더 자주 변하기 때문입니다. 예를 들어 테스트 데이터베이스에서 코드를 테스트하려는 경우

DI는 객체 지향 프로그래밍의 세계에서 이것과 유사합니다. 상수 리터럴 대신 값은 전체 객체입니다. 그러나 클래스 코드에서 코드를 생성하는 코드를 이동시키는 이유는 비슷합니다. 객체는 더 자주 변경된 것보다 더 자주 변경됩니다. 그러한 변화가 필요한 중요한 사례 중 하나는 테스트입니다.

간단한 예제를 시도해 봅시다 자동차 그리고 엔진 클래스, 모든 자동차는 적어도 지금은 어디에서나 갈 엔진이 필요합니다. 따라서 의존성 주입없이 코드가 어떻게 보이는가.

public class Car
{
    public Car()
    {
        GasEngine engine = new GasEngine();
        engine.Start();
    }
}

public class GasEngine
{
    public void Start()
    {
        Console.WriteLine("I use gas as my fuel!");
    }
}

그리고 자동차 수업을 인스턴스화하기 위해 다음 코드를 사용합니다.

Car car = new Car();

이 코드의 문제는 가스 엔진과 밀접하게 결합되었으며 ElectricityEngine으로 변경하기로 결정하면 자동차 클래스를 다시 작성해야합니다. 그리고 응용 프로그램이 클수록 새로운 유형의 엔진을 추가하고 사용해야 할 더 많은 문제와 두통이 필요합니다.

다시 말해,이 접근법의 말로, 우리의 고급 자동차 클래스는 고체에서 의존성 반전 원리 (DIP)를 위반하는 하위 레벨 가스 엔진 클래스에 의존한다는 것입니다. DIP는 우리가 구체적인 클래스가 아닌 추상화에 의존해야한다고 제안합니다. 따라서이를 충족시키기 위해 Iengine 인터페이스를 소개하고 다음과 같이 코드를 다시 작성합니다.

    public interface IEngine
    {
        void Start();
    }

    public class GasEngine : IEngine
    {
        public void Start()
        {
            Console.WriteLine("I use gas as my fuel!");
        }
    }

    public class ElectricityEngine : IEngine
    {
        public void Start()
        {
            Console.WriteLine("I am electrocar");
        }
    }

    public class Car
    {
        private readonly IEngine _engine;
        public Car(IEngine engine)
        {
            _engine = engine;
        }

        public void Run()
        {
            _engine.Start();
        }
    }

이제 자동차 클래스는 엔진의 특정 구현이 아니라 Iengine 인터페이스에만 의존합니다. 이제 유일한 요령은 자동차 인스턴스를 만드는 방법과 가스 엔진 또는 전기 엔진과 같은 실제 콘크리트 엔진 클래스를 제공하는 방법입니다. 그게 어디에 있습니다 의존성 주입 들어 온다.

   Car gasCar = new Car(new GasEngine());
   gasCar.Run();
   Car electroCar = new Car(new ElectricityEngine());
   electroCar.Run();

여기서 우리는 기본적으로 자동차 생성자에 종속성 (엔진 인스턴스)을 주입 (통과)합니다. 따라서 이제 우리의 클래스는 객체와 그 종속성 사이에 느슨한 커플 링을 제공하며 자동차 클래스를 변경하지 않고도 새로운 유형의 엔진을 쉽게 추가 할 수 있습니다.

의 주요 이점 의존성 주입 이 클래스는 하드 코딩 된 종속성이 없기 때문에 더 느슨하게 결합됩니다. 이것은 위에서 언급 한 종속성 반전 원리를 따릅니다. 특정 구현을 참조하는 대신 클래스는 추상화를 요청합니다 (일반적으로 인터페이스) 클래스가 구성 될 때 그들에게 제공됩니다.

그래서 결국 의존성 주입 객체와 그 종속성 사이의 느슨한 커플 링을 달성하는 기술 일뿐입니다. 클래스가 행동을 수행하기 위해 필요한 종속성을 직접 인스턴스화하기보다는 생성자 주입을 통해 클래스 (대부분 종종)에 종속성이 제공됩니다.

또한 많은 종속성이있는 경우 IOC (Control) 컨테이너를 사용하는 것이 매우 좋습니다.이 컨테이너는 모든 종속성에 대한 구체적인 구현이 어떤 인터페이스를 매핑 해야하는지 알 수 있으며 구성 할 때 해당 종속성을 해결할 수 있습니다. 우리의 대상. 예를 들어, 우리는 IOC 컨테이너의 매핑을 Iengine 의존성은 가스 엔진 클래스와 우리가 IOC 컨테이너에게 우리의 인스턴스를 요청할 때 자동차 클래스는 자동으로 우리의 구성을 구성합니다 자동차 a 가스 엔진 의존성이 통과되었습니다.

업데이트: Julie Lerman의 EF Core에 대한 코스를보고 DI에 대한 짧은 정의를 좋아했습니다.

종속성 주입은 응용 프로그램이 해당 클래스를 해당 객체에 책임을 지도록 강요하지 않으면 서 응용 프로그램이 필요한 클래스에 즉시 객체를 주입 할 수 있도록하는 패턴입니다. 코드가 더 느슨하게 결합 될 수 있으며 엔티티 프레임 워크 코어 가이 동일한 서비스 시스템에 연결됩니다.

낚시 가고 싶다고 상상해 봅시다 :

  • 의존성 주입 없이는 모든 것을 직접 돌봐야합니다. 당신은 보트를 찾고, 낚시 막대를 사고, 미끼 등을 찾기 위해서는 필요합니다. 물론 가능하지만, 그것은 당신에게 많은 책임을집니다. 소프트웨어 측면에서, 이는이 모든 것을 조회해야한다는 것을 의미합니다.

  • 의존성 주입을 통해 다른 사람이 모든 준비를 처리하고 필요한 장비를 귀하에게 제공합니다. 당신은 보트, 낚시 막대 및 미끼를 모두 사용할 준비가되어 있습니다.

이것 가장 간단한 설명입니다 의존성 주입 그리고 의존성 주입 컨테이너 나는 본 적이있다 :

의존성 주입없이

  • 응용 프로그램에는 foo (예 : 컨트롤러)가 필요합니다.
  • 응용 프로그램은 foo를 만듭니다
  • 응용 프로그램은 foo를 호출합니다
    • Foo는 바 (예 : 서비스)가 필요합니다.
    • Foo는 바를 만듭니다
    • Foo는 막대를 호출합니다
      • 막대는 BIM (서비스, 저장소 등)이 필요합니다.
      • 바는 bim을 만듭니다
      • 바는 뭔가를합니다

의존성 주입

  • 응용 프로그램에는 BIM이 필요한 막대가 필요한 foo가 필요합니다.
  • 응용 프로그램은 BIM을 만듭니다
  • 응용 프로그램은 막대를 생성하고 BIM을 제공합니다
  • 응용 프로그램은 foo를 생성하고 막대를 제공합니다
  • 응용 프로그램은 foo를 호출합니다
    • Foo는 막대를 호출합니다
      • 바는 뭔가를합니다

의존성 주입 컨테이너 사용

  • 응용 프로그램은 foo가 필요합니다 :
  • 응용 프로그램은 컨테이너에서 foo를 얻습니다.
    • 컨테이너는 BIM을 만듭니다
    • 컨테이너는 막대를 생성하고 BIM을 제공합니다
    • 컨테이너는 foo를 생성하고 막대를 제공합니다
  • 응용 프로그램은 foo를 호출합니다
    • Foo는 막대를 호출합니다
      • 바는 뭔가를합니다

의존성 주입 그리고 의존성 주입 컨테이너 다른 것 :

  • 종속성 주입은 더 나은 코드를 작성하는 방법입니다
  • DI 컨테이너는 종속성을 주입하는 데 도움이되는 도구입니다.

의존성 주입을하기 위해 컨테이너가 필요하지 않습니다. 그러나 컨테이너가 도움이 될 수 있습니다.

"종속성 주입"이 매개 변수화 된 생성자와 공개 세터를 사용하는 것을 의미하지 않습니까?

James Shore의 기사는 다음과 같은 예를 보여줍니다..

의존성 주입이없는 생성자 :

public class Example { 
  private DatabaseThingie myDatabase; 

  public Example() { 
    myDatabase = new DatabaseThingie(); 
  } 

  public void doStuff() { 
    ... 
    myDatabase.getData(); 
    ... 
  } 
} 

의존성 주입이있는 생성자 :

public class Example { 
  private DatabaseThingie myDatabase; 

  public Example(DatabaseThingie useThisDatabaseInstead) { 
    myDatabase = useThisDatabaseInstead; 
  }

  public void doStuff() { 
    ... 
    myDatabase.getData(); 
    ... 
  } 
}

의존성 주입 (DI)이란 무엇입니까?

다른 사람들이 말했듯이 의존성 주입 (DI) 직접 창출의 책임을 제거하고 수명의 관리, 우리의 관심 부류 (소비자 클래스)가 의존하는 다른 객체 사례 ( 음 감각). 이러한 인스턴스는 대신 소비자 클래스, 일반적으로 생성자 매개 변수 또는 속성 세터를 통해 전달됩니다 (소비자 클래스로 전달하는 종속성 객체의 관리는 일반적으로 제어 역전 (IOC) 컨테이너이지만 다른 주제입니다).

DI, 딥 및 견고한

특히 로버트 C 마틴의 패러다임에서 객체 지향 디자인의 견고한 원리, DI 가능한 구현 중 하나입니다 종속성 반전 원리 (DIP). 그만큼 딥은 DSOLID 만트라 - 다른 딥 구현에는 서비스 로케이터 및 플러그인 패턴이 포함됩니다.

딥의 목적은 클래스간에 단단하고 구체적인 의존성을 분리하고 대신에 interface, abstract class 또는 pure virtual class, 사용 된 언어와 접근법에 따라.

DIP가 없으면 우리의 코드 (이것을 '소비 클래스'라고 불렀음)는 구체적인 의존성과 직접적으로 결합되며 종종이 의존성을 얻고 관리하는 방법을 아는 방법을 개념적으로 알리는 책임이 있습니다.

"I need to create/use a Foo and invoke method `GetBar()`"

DIP를 적용한 후에는 요구 사항이 완화되고 수명을 얻고 관리하는 것에 대한 우려 Foo 의존성이 제거되었습니다.

"I need to invoke something which offers `GetBar()`"

DIP (및 DI)를 사용하는 이유는 무엇입니까?

이러한 방식으로 클래스 간의 종속성을 분리하면 허용됩니다 쉬운 대체 추상화의 전제 조건을 충족시키는 다른 구현을 가진 이러한 종속성 클래스 중 (예 : 동일한 인터페이스의 다른 구현으로 의존성을 전환 할 수 있음). 더욱이 다른 사람들이 언급했듯이 아마도 가능합니다 그만큼 DIP를 통해 클래스를 분리하는 가장 일반적인 이유는 이제 동일한 종속성을 스텁 및/또는 조롱 할 수 있기 때문에 소비 클래스를 분리하여 테스트 할 수 있기 때문입니다.

DI의 결과는 종속성 객체가 소비 클래스 (생성자 또는 세터 주입을 통해)로 전달되므로 의존성 개체 인스턴스의 수명 관리가 더 이상 소비 클래스에 의해 제어되지 않는다는 것입니다.

이것은 다른 방식으로 볼 수 있습니다.

  • 소비 클래스에 의한 의존성에 대한 수명 제어를 유지 해야하는 경우, 의존성 클래스 인스턴스를 소비자 클래스에 생성하기위한 (추상) 공장을 주입하여 제어를 다시 설정할 수 있습니다. 소비자는 Create 필요에 따라 공장에서,이 사례를 한 번 완료 한 경우 처리하십시오.
  • 또는 의존성 인스턴스의 수명 제어는 IOC 컨테이너로 포기 될 수 있습니다 (아래에 대한 자세한 내용).

언제 DI를 사용합니까?

  • 동등한 구현을 위해 종속성을 대체 할 필요가있는 경우
  • 종속성을 분리하여 클래스 방법을 단위 테스트해야 할 때마다
  • 의존성 수명의 불확실성이 실험을 보증 할 수있는 경우 (예 : 이봐, MyDepClass 스레드가 안전합니까 - 우리가 싱글 톤으로 만들고 모든 소비자에게 동일한 인스턴스를 주입한다면 어떻게해야합니까?)

예시

간단한 C# 구현은 다음과 같습니다. 아래의 소비 수업을 감안할 때 :

public class MyLogger
{
   public void LogRecord(string somethingToLog)
   {
      Console.WriteLine("{0:HH:mm:ss} - {1}", DateTime.Now, somethingToLog);
   }
}

무해한 것처럼 보이지만 두 가지가 있습니다 static 다른 두 클래스에 대한 의존성, System.DateTime 그리고 System.Console, 로깅 출력 옵션을 제한 할뿐만 아니라 (아무도 보지 않으면 콘솔에 로깅이 가치가 없음), 더 나쁜 것은 비 결정적 시스템 시계에 대한 의존성을 고려할 때 자동으로 테스트하기가 어렵습니다.

그러나 우리는 신청할 수 있습니다 DIP 이 클래스에, 타임 스탬핑의 관심을 의존성으로 추상화하고 커플 링 MyLogger 간단한 인터페이스에만 :

public interface IClock
{
    DateTime Now { get; }
}

우리는 또한 의존성을 풀 수 있습니다 Console a와 같은 추상화에 TextWriter. 의존성 주입은 일반적으로 어느 쪽이든 구현됩니다 constructor 주입 (소비 클래스의 생성자에 대한 매개 변수로 의존성으로 추상화를 전달) 또는 Setter Injection (a를 통해 의존성을 전달합니다 setXyz() 세터 또는 .NET 속성이 있습니다 {set;} 한정된). 이는 클래스가 구성 후 올바른 상태에있게되며 내부 종속성 필드를 readonly (C#) 또는 final (자바). 따라서 위의 예에서 생성자 주입을 사용하면 다음과 같이됩니다.

public class MyLogger : ILogger // Others will depend on our logger.
{
    private readonly TextWriter _output;
    private readonly IClock _clock;

    // Dependencies are injected through the constructor
    public MyLogger(TextWriter stream, IClock clock)
    {
        _output = stream;
        _clock = clock;
    }

    public void LogRecord(string somethingToLog)
    {
        // We can now use our dependencies through the abstraction 
        // and without knowledge of the lifespans of the dependencies
        _output.Write("{0:yyyy-MM-dd HH:mm:ss} - {1}", _clock.Now, somethingToLog);
    }
}

(콘크리트 Clock 물론 복귀 할 수있는 것은 제공해야합니다. DateTime.Now, 그리고 두 가지 종속성은 생성자 주입을 통해 IOC 컨테이너에 의해 제공되어야합니다)

자동화 된 단위 테스트를 구축 할 수 있으며, 이는 이제 종속성을 제어 할 수 있으므로 로거가 올바르게 작동하고 있음을 확실히 증명할 수 있습니다.

[Test]
public void LoggingMustRecordAllInformationAndStampTheTime()
{
    // Arrange
    var mockClock = new Mock<IClock>();
    mockClock.Setup(c => c.Now).Returns(new DateTime(2015, 4, 11, 12, 31, 45));
    var fakeConsole = new StringWriter();

    // Act
    new MyLogger(fakeConsole, mockClock.Object)
        .LogRecord("Foo");

    // Assert
    Assert.AreEqual("2015-04-11 12:31:45 - Foo", fakeConsole.ToString());
}

다음 단계

의존성 주입은 항상 an과 관련이 있습니다 제어 용기의 역전 (IOC), 콘크리트 의존성 인스턴스를 주입 (제공)하고 수명 인스턴스를 관리합니다. 구성 / 부트 스트래핑 프로세스 중에 IoC 컨테이너는 다음을 정의 할 수 있습니다.

  • 각 추상화와 구성된 콘크리트 구현 사이의 매핑 (예 : "소비자가 요청할 때마다 IBar, 반환 a ConcreteBar 사례")
  • 각 소비자 인스턴스에 대한 새 객체를 만들고 모든 소비자의 싱글 톤 종속성 인스턴스를 공유하고 동일한 스레드에서 동일한 종속성 인스턴스를 공유하려면 각 소비자 인스턴스에 대한 새 개체를 만들기위한 각 종속성의 수명 관리를 위해 정책을 설정할 수 있습니다.
  • .NET에서 IOC 컨테이너는 다음과 같은 프로토콜을 알고 있습니다. IDisposable 그리고 책임을 맡을 것입니다 Disposing 구성된 수명 관리에 따라 의존성.

일반적으로 IOC 컨테이너가 구성 / 부트 스트랩 된 후에는 백그라운드에서 원활하게 작동하여 코더가 종속성에 대해 걱정하지 않고 현재 코드에 집중할 수 있습니다.

Di-Friendly 코드의 핵심은 클래스의 정적 커플 링을 피하고 종속성 생성을 위해 새로운 ()를 사용하지 않는 것입니다.

위의 예에 따르면, 종속성의 분리에는 약간의 설계 노력이 필요하며 개발자에게는 습관을 깨는 데 필요한 패러다임 전환이 있습니다. new의존성을 직접적으로 ing하고 대신 의존성을 관리하기 위해 컨테이너를 신뢰합니다.

그러나 이점은 특히 관심 부류를 철저히 테스트 할 수있는 능력에 있습니다.

메모 : 생성 / 매핑 / 프로젝션 (비아 new ..())의 poco / pojo / serialization dtos / entity 그래프 / 익명 JSON Projection et al -IE "데이터 전용"클래스 또는 레코드 - 사용 또는 반환 된 메소드가 ~ 아니다 의존성 (UML 의미에서)으로 간주되고 DI가 적용되지 않습니다. 사용 new 이것들을 투영하는 것은 괜찮습니다.

의존성 주입 개념을 이해하기 간단하게 만듭니다. 전구를 전환 (켜기/끄기)으로 스위치 버튼의 예를 들어 보겠습니다.

의존성 주입없이

스위치는 내가 어떤 전구에 연결되어 있는지 미리 알아야합니다 (하드 코딩 된 종속성). 그래서,

스위치 -> 영구 율 // 스위치가 영구 전구에 직접 연결되어있어 쉽게 테스트 할 수 없습니다.

Switch(){
PermanentBulb = new Bulb();
PermanentBulb.Toggle();
}

의존성 주입

스위치는 전구가 전달되는 전구를 켜거나 끄는 것만으로 만 알고 있습니다. 그래서,

스위치 -> Bulb1 또는 Bulb2 또는 NightBulb (주입 된 종속성)

Switch(AnyBulb){ //pass it whichever bulb you like
AnyBulb.Toggle();
}

수정 제임스 스위치 및 전구의 예 :

public class SwitchTest { 
  TestToggleBulb() { 
    MockBulb mockbulb = new MockBulb(); 

    // MockBulb is a subclass of Bulb, so we can 
    // "inject" it here: 
    Switch switch = new Switch(mockBulb); 

    switch.ToggleBulb(); 
    mockBulb.AssertToggleWasCalled(); 
  } 
}

public class Switch { 
  private Bulb myBulb; 

  public Switch() { 
    myBulb = new Bulb(); 
  } 

  public Switch(Bulb useThisBulbInstead) { 
    myBulb = useThisBulbInstead; 
  } 

  public void ToggleBulb() { 
    ... 
    myBulb.Toggle(); 
    ... 
  } 
}`

종속성 주입 (DI)의 요점은 응용 프로그램 소스 코드를 유지하는 것입니다. 깨끗한 그리고 안정적인:

  • 깨끗한 종속성 초기화 코드
  • 안정적인 사용 된 의존성에 관계없이

실제로, 모든 설계 패턴은 미래의 변화가 최소 파일에 영향을 미치도록 우려 사항을 분리합니다.

DI의 특정 도메인은 종속성 구성 및 초기화의 대표입니다.

예 : 쉘 스크립트가있는 DI

가끔 자바 밖에서 일한다면 어떻게 source 종종 많은 스크립팅 언어 (쉘, TCL 등 또는 심지어 import 파이썬 에서이 목적을 위해 잘못 사용).

간단한 것을 고려하십시오 dependent.sh 스크립트:

#!/bin/sh
# Dependent
touch         "one.txt" "two.txt"
archive_files "one.txt" "two.txt"

스크립트는 의존적입니다. 자체적으로 성공적으로 실행되지 않습니다 (archive_files 정의되지 않음).

당신은 정의합니다 archive_files 안에 archive_files_zip.sh 구현 스크립트 (사용 zip 이 경우) :

#!/bin/sh
# Dependency
function archive_files {
    zip files.zip "$@"
}

대신에 source-의존적 스크립트에서 직접 구현 스크립트를 사용하면 injector.sh 두 "구성 요소"를 감싸는 "컨테이너":

#!/bin/sh 
# Injector
source ./archive_files_zip.sh
source ./dependent.sh

그만큼 archive_files 의존 방금 그렇습니다 주사 ~ 안으로 매달린 스크립트.

어떤 구현이든 의존성을 주입 할 수 있습니다 archive_files 사용 tar 또는 xz.

예 : DI 제거

만약에 dependent.sh 스크립트 사용 된 종속성 직접적으로 접근 방식이 호출됩니다. 종속성 조회 (반대입니다 의존성 주입):

#!/bin/sh
# Dependent

# dependency look-up
source ./archive_files_zip.sh

touch         "one.txt" "two.txt"
archive_files "one.txt" "two.txt"

이제 문제는 종속 "구성 요소"가 초기화 자체를 수행해야한다는 것입니다.

"구성 요소"의 소스 코드는 깨끗한 ...도 아니다 안정적인 종속성 초기화의 모든 변경에는 "구성 요소"의 소스 코드 파일에 대한 새로운 릴리스가 필요하기 때문입니다.

마지막 말

DI는 Java 프레임 워크에서만큼 크게 강조되고 대중화되지 않습니다.

그러나 그것은 다음에 대한 우려를 분할하는 일반적인 접근법입니다.

  • 신청 개발 (하나의 소스 코드 릴리스 라이프 사이클)
  • 신청 전개 (다수의 독립적 인 라이프 사이클이있는 대상 환경)

구성 만 사용합니다 종속성 조회 구성 매개 변수 수는 지원되는 종속성 유형 (예 : 새로운 데이터베이스 유형)뿐만 아니라 종속성 당 (예 : 새로운 인증 유형) 당 변경 될 수 있으므로 도움이되지 않습니다.

위의 모든 답변은 훌륭합니다. 제 목표는 프로그래밍 지식이없는 사람이라면 누구나 개념을 이해할 수 있도록 개념을 간단한 방식으로 설명하는 것입니다.

의존성 주입은 더 간단한 방식으로 복잡한 시스템을 만드는 데 도움이되는 설계 패턴 중 하나입니다.

우리는 일상 생활 에서이 패턴의 다양한 적용을 볼 수 있습니다. 예 중 일부는 테이프 레코더, VCD, CD 드라이브 등입니다.

Reel-to-reel portable tape recorder, mid-20th century.

위의 이미지는 20 세기 중반의 릴 투 릴 휴대용 테이프 레코더의 이미지입니다. 원천.

테이프 레코더 머신의 주요 의도는 녹음 또는 재생 사운드입니다.

시스템을 설계하는 동안 사운드 또는 음악을 녹음하거나 재생하려면 릴이 필요합니다. 이 시스템을 설계 할 수있는 두 가지 가능성이 있습니다

  1. 릴을 기계 안에 넣을 수 있습니다
  2. 우리는 릴을 배치 할 수있는 릴에 대한 후크를 제공 할 수 있습니다.

첫 번째를 사용하는 경우 릴을 변경하려면 기계를 열어야합니다. 우리가 릴을위한 후크를 배치하는 두 번째 것을 선택한다면, 릴을 바꾸어 음악을 재생하는 데 더 많은 이점을 얻고 있습니다. 또한 릴의 모든 것을 재생하는 것만으로 기능을 줄입니다.

현명한 의존성 주입과 마찬가지로 독립적 인 구성 요소를 결합하여 복잡한 시스템을 형성 할 수 있도록 구성 요소의 특정 기능에만 초점을 맞추기 위해 종속성을 외부화하는 프로세스입니다.

의존성 주입을 사용하여 달성 한 주요 이점.

  • 높은 응집력과 느슨한 커플 링.
  • 의존성 외부화 및 책임 만 바라본다.
  • 물건을 구성 요소로 만들고 결합하여 높은 기능을 갖춘 대형 시스템을 형성합니다.
  • 고품질 구성 요소가 독립적으로 개발되었으므로 올바르게 테스트 되었기 때문에 개발하는 데 도움이됩니다.
  • 부품이 실패하면 구성 요소를 다른 것으로 바꾸는 데 도움이됩니다.

이제 이러한 개념은 프로그래밍 세계에서 잘 알려진 프레임 워크의 기초를 형성합니다. 스프링 앵귤러 등은이 개념의 상단에 구축 된 잘 알려진 소프트웨어 프레임 워크입니다.

종속성 주입은 다른 객체가 컴파일 시간을 모르면 해당 기능을 제공하는 데 사용될 클래스 또는 단순히 객체에 특성을 주입하는 방법을 의존성 주입이라고하는 데 사용되는 객체의 인스턴스를 만드는 데 사용되는 패턴입니다.

의존성 주입의 예

이전에는 이와 같은 코드를 작성하고 있습니다

Public MyClass{
 DependentClass dependentObject
 /*
  At somewhere in our code we need to instantiate 
  the object with new operator  inorder to use it or perform some method.
  */ 
  dependentObject= new DependentClass();
  dependentObject.someMethod();
}

종속성 주입을 통해 종속성 인젝터가 우리를위한 인스턴스화를 제거합니다.

Public MyClass{
 /* Dependency injector will instantiate object*/
 DependentClass dependentObject

 /*
  At somewhere in our code we perform some method. 
  The process of  instantiation will be handled by the dependency injector
 */ 

  dependentObject.someMethod();
}

당신은 또한 읽을 수 있습니다

대조군의 역전 및 의존성 주입의 차이

의존성 주입이란 무엇입니까?

의존성 주입 (DI)은 서로 의존하는 물체를 분리하는 것을 의미합니다. 객체 A는 객체 B에 의존한다고 가정하므로 아이디어는 이러한 객체를 서로 분리하는 것입니다. 컴파일 시간에도 불구하고 런타임에 객체에 종속성을 공유하는 새로운 키워드를 사용하여 객체를 하드 코드 할 필요가 없습니다. 우리가 이야기한다면

봄에 의존성 주입의 작동 방식 :

새 키워드를 사용하여 객체를 하드 코드 할 필요가 없습니다. 구성 파일에서 Bean 종속성을 정의합니다. 스프링 컨테이너는 모두 연결을 담당합니다.

제어 역전 (IOC)

IOC는 일반적인 개념이며 다양한 방식으로 표현 될 수 있으며 종속성 주입은 IOC의 구체적인 예입니다.

두 가지 유형의 종속성 주입 :

  1. 생성자 주입
  2. 세터 주입

1. 생성자 기반 의존성 주입 :

생성자 기반 DI는 컨테이너가 여러 인수가있는 클래스 생성자를 호출 할 때 달성되며, 각각의 다른 클래스에 대한 의존성을 나타냅니다.

public class Triangle {

private String type;

public String getType(){
    return type;
 }

public Triangle(String type){   //constructor injection
    this.type=type;
 }
}
<bean id=triangle" class ="com.test.dependencyInjection.Triangle">
        <constructor-arg value="20"/>
  </bean>

2. 세터 기반 의존성 주입 :

Setter 기반 DI는 콩을 인스턴스화하기 위해 연락이없는 생성자 또는 무사 정적 공장 방법을 호출 한 후 콩의 컨테이너 호출 세터 방법에 의해 달성됩니다.

public class Triangle{

 private String type;

 public String getType(){
    return type;
  }
 public void setType(String type){          //setter injection
    this.type = type;
  }
 }

<!-- setter injection -->
 <bean id="triangle" class="com.test.dependencyInjection.Triangle">
        <property name="type" value="equivialteral"/>

참고 : 의무 종속성에 대한 생성자 인수와 선택적 종속성에 대한 세터를 사용하는 것이 좋습니다. 세터에서 @Required 주석보다 주석을 기반으로하는 경우 세터를 사용하여 세터를 필요한 종속성으로 만들 수 있습니다.

내가 생각할 수있는 가장 유사한 비유는 외과 의사와 그의 조수 (들)는 외과 의사가 주된 사람이자 그의 조수이며 외과 의사가 하나에 집중할 수 있도록 다양한 수술 요소를 제공하는 주요 사람입니다. 그가 가장 잘하는 일 (수술). 조수가 없으면 외과 의사는 필요할 때마다 구성 요소를 직접 가져와야합니다.

짧은 DI는 부양 가족을 제공하여 구성 요소를 제공하여 구성 요소에 대한 일반적인 추가 책임 (부담)을 제거하는 기술입니다.

DI surgeon who can concentrate on surgery.

DI를 사용하는시기 : 거의 모든 생산 프로젝트 (소규모/큰), 특히 변화하는 비즈니스 환경에서 DI를 사용하는 것이 좋습니다 :)

이유 : 코드를 쉽게 테스트 할 수 있고 조롱 가능한 등을 쉽게 테스트하여 변경 사항을 신속하게 테스트하고 시장에 밀어 넣을 수 있기를 원하기 때문입니다. 더 많은 제어권이있는 코드베이스로의 여정에서 당신을 지원할 멋진 무료 도구/프레임 워크가 많이있을 때 왜 그런지에 대한 이유 외에.

이는 객체가 작업을 수행하는 데 필요한만큼 의존성 만 있어야하며 종속성은 적어야한다는 것을 의미합니다. 또한 객체의 종속성은 가능한 경우 "콘크리트"객체가 아닌 인터페이스에 있어야합니다. (콘크리트 객체는 키워드 새로 생성 된 모든 객체입니다.) 느슨한 커플 링은 재사용 성이 높고 유지 관리가 쉬워지며 값 비싼 서비스 대신 쉽게 "모의"객체를 제공 할 수 있습니다.

"의존성 주입"(DI)은 "제어의 역전"(IOC)이라고도하며,이 느슨한 커플 링을 장려하는 기술로 사용될 수 있습니다.

DI 구현에는 두 가지 주요 접근 방식이 있습니다.

  1. 생성자 주입
  2. 세터 주입

생성자 주입

객체 의존성을 생성자에게 전달하는 기술입니다.

생성자는 콘크리트 객체가 아닌 인터페이스를 수용합니다. 또한 OrderDAO 매개 변수가 NULL 인 경우 예외가 발생합니다. 이것은 유효한 의존성을받는 것이 중요하다는 것을 강조합니다. 제작자 주입은 제 생각에 객체에 종속성을 제공하는 선호되는 메커니즘입니다. 적절한 실행을 위해 "사람"객체에 의존성을 제공 해야하는 객체를 호출하는 동안 개발자에게 분명합니다.

세터 주입

그러나 다음 예를 고려하십시오… 종속성이없는 10 가지 방법이있는 클래스가 있다고 가정하지만 IDAO에 의존하는 새로운 메소드를 추가하고 있다고 가정하십시오. 생성자를 변경하여 생성자 주입을 사용하도록 변경할 수 있지만 모든 생성자 통화의 변경을 강요 할 수 있습니다. 또는 종속성을 취하는 새로운 생성자를 추가 할 수 있지만 개발자는 한 생성자를 다른 생성자보다 사용하는시기를 어떻게 쉽게 알 수 있습니까? 마지막으로, 의존성이 생성하는 데 매우 비싸면, 드물게 사용될 수있을 때 생성자에게 왜 생성되고 전달되어야합니까? "Setter Injection"은 이와 같은 상황에서 사용할 수있는 또 다른 DI 기술입니다.

세터 주입은 의존성을 생성자에게 전달하도록 강요하지 않습니다. 대신, 종속성은 도움이 필요한 객체에 의해 노출 된 공개 속성으로 설정됩니다. 이전에 암시 된 바와 같이,이를 수행하기위한 주요 동기는 다음과 같습니다.

  1. 레거시 클래스의 생성자를 수정하지 않고 의존성 주입 지원.
  2. 값 비싼 리소스 나 서비스를 최대한 늦게 그리고 필요할 때만 만들 수 있습니다.

위의 코드가 어떻게 보이는지에 대한 예는 다음과 같습니다.

public class Person {
    public Person() {}

    public IDAO Address {
        set { addressdao = value; }
        get {
            if (addressdao == null)
              throw new MemberAccessException("addressdao" +
                             " has not been initialized");
            return addressdao;
        }
    }

    public Address GetAddress() {
       // ... code that uses the addressdao object
       // to fetch address details from the datasource ...
    }

    // Should not be called directly;
    // use the public property instead
    private IDAO addressdao;

예를 들어, 우리는 2 개의 클래스가 있습니다 Client 그리고 Service. Client 사용합니다 Service

public class Service {
    public void doSomeThingInService() {
        // ...
    }
}

의존성 주입없이

방법 1)

public class Client {
    public void doSomeThingInClient() {
        Service service = new Service();
        service.doSomeThingInService();
    }
}

방법 2)

public class Client {
    Service service = new Service();
    public void doSomeThingInClient() {
        service.doSomeThingInService();
    }
}

방법 3)

public class Client {
    Service service;
    public Client() {
        service = new Service();
    }
    public void doSomeThingInClient() {
        service.doSomeThingInService();
    }
}

1) 2) 3) 사용

Client client = new Client();
client.doSomeThingInService();

장점

  • 단순한

단점

  • 테스트하기 어렵습니다 Client 수업
  • 우리가 변할 때 Service 생성자, 우리는 모든 장소에서 코드를 변경해야합니다. Service 물체

의존성 주입을 사용하십시오

방법 1) 생성자 주입

public class Client {
    Service service;

    Client(Service service) {
        this.service = service;
    }

    // Example Client has 2 dependency 
    // Client(Service service, IDatabas database) {
    //    this.service = service;
    //    this.database = database;
    // }

    public void doSomeThingInClient() {
        service.doSomeThingInService();
    }
}

사용

Client client = new Client(new Service());
// Client client = new Client(new Service(), new SqliteDatabase());
client.doSomeThingInClient();

방법 2) 세터 주입

public class Client {
    Service service;

    public void setService(Service service) {
        this.service = service;
    }

    public void doSomeThingInClient() {
        service.doSomeThingInService();
    }
}

사용

Client client = new Client();
client.setService(new Service());
client.doSomeThingInClient();

방법 3) 인터페이스 주입

확인하다 https://en.wikipedia.org/wiki/dependency_injection

===

이제이 코드는 이미 따릅니다 Dependency Injection 그리고 테스트가 더 쉽습니다 Client 수업.
그러나 우리는 여전히 사용합니다 new Service() 많은 시간과 변화 할 때 좋지 않습니다 Service 건설자. 이를 방지하기 위해 DI 인젝터를 사용할 수 있습니다.
1) 간단한 매뉴얼 Injector

public class Injector {
    public static Service provideService(){
        return new Service();
    }

    public static IDatabase provideDatatBase(){
        return new SqliteDatabase();
    }
    public static ObjectA provideObjectA(){
        return new ObjectA(provideService(...));
    }
}

사용

Service service = Injector.provideService();

2) 도서관 사용 : Android 용 Dagger2

장점

  • 테스트를 더 쉽게 만드십시오
  • 당신이 변경할 때 Service, 인젝터 클래스에서만 변경하면됩니다.
  • 사용하는 경우 Constructor Injection, 당신이 생성자를 볼 때 Client, 당신은 얼마나 많은 의존성을 볼 수 있습니다 Client 수업

단점

  • 사용하는 경우 Constructor Injection,, Service 객체가 생성됩니다 Client 생성, 언젠가 우리는 기능을 사용합니다 Client 사용하지 않고 수업 Service 그래서 만들어졌습니다 Service 낭비됩니다

의존성 주입 정의

https://en.wikipedia.org/wiki/dependency_injection

종속성은 사용할 수있는 객체입니다 (Service)
주입은 의존성을 통과하는 것입니다 (Service) 종속 물체에 (Client) 그것은 그것을 사용할 것입니다

모두가 DI를 위해 썼기 때문에 몇 가지 질문을하겠습니다 ..

  1. 클래스 (예 : 컨트롤러에 대한 서비스를 위해)에 주입 될 모든 실제 구현 (인터페이스가 아님)이 어떤 종류의 하드 코딩이 아닌가?
  2. 런타임에 객체를 변경하려면 어떻게해야합니까? 예를 들어, MyController를 인스턴스화 할 때 이미 Config가 표시되어 FileLogger를 Ilogger로 주입합니다. 그러나 나는 DatabasElogger를 주입하고 싶을 수도 있습니다.
  3. Aclass가 필요한 객체를 변경하고 싶을 때마다 이제 클래스 자체와 구성 파일의 두 곳을 살펴 봐야합니다. 그것은 어떻게 인생을 더 쉽게 만들 수 있습니까?
  4. Aclass의 후보자가 주입되지 않으면 그것을 조롱하기가 더 어렵습니까?
  5. 첫 번째 질문으로 돌아갑니다. 새 개체를 사용하는 경우 나쁘면 인터페이스가 아닌 구현을 어떻게 주입합니까? 많은 사람들이 우리가 실제로 인터페이스를 주입하고 있다고 말하고 있지만 구성을 통해 해당 인터페이스의 구현을 지정할 수 있습니다.

이것은 @adam n에 게시 된 답변을 기반으로합니다.

Personservice가 더 이상 GroupMembershipservice에 대해 걱정할 필요가없는 이유는 무엇입니까? 방금 Groupmembership에 여러 가지 (개체/속성)가 있다고 언급했습니다. PSERVICE에서 GMSERVICE가 필요한 경우 재산으로 가질 것입니다. 주입했는지 여부에 관계없이 그것을 조롱 할 수 있습니다. 내가 주입하기를 원하는 유일한 시간은 GMSERVICE에 더 구체적인 어린이 수업이있는 경우 런타임까지 알 수없는 것입니다. 그런 다음 서브 클래스를 주입하고 싶을 것입니다. 또는 싱글 톤 또는 프로토 타입으로 사용하려는 경우. 솔직히 말해서, 구성 파일에는 컴파일 시간 동안 주입 할 유형 (인터페이스)의 하위 클래스에 따라 모든 것이 하드 코딩되어 있습니다.

편집하다

Di에 Jose Maria Arranz의 좋은 의견

DI는 종속성의 방향을 결정하고 접착제 코드를 작성해야 할 필요성을 제거하여 응집력을 증가시킵니다.

거짓. 종속 방향은 XML 형식 또는 주석으로, 종속성은 XML 코드 및 주석으로 작성됩니다. XML 및 주석은 소스 코드입니다.

DI는 모든 구성 요소를 모듈화하여 (즉, 교체 가능)로 만들어 커플 링을 줄이고 서로 잘 정의 된 인터페이스를 가지고 있습니다.

거짓. 인터페이스를 기반으로 모듈 식 코드를 작성하려면 DI 프레임 워크가 필요하지 않습니다.

교체 가능한 정보 : 매우 간단한 .properties 아카이브 및 클래스를 사용하면 Wich 클래스를 정의 할 수 있습니다. 코드 클래스의 클래스를 변경할 수있는 경우 Java는 귀하를위한 것이 아니며 스크립팅 언어를 사용하십시오. 그건 그렇고 : 다시 컴파일하지 않고 주석을 변경할 수 없습니다.

제 생각에는 DI 프레임 워크에 대한 유일한 이유 : 보일러 플레이트 감소. 잘 완성 된 공장 시스템을 사용하면 선호하는 DI 프레임 워크와 동일하고 통제력이 있으며 예측 가능한 DI 프레임 워크는 코드 감소를 약속합니다 (XML 및 주석도 소스 코드이기도합니다). 문제는이 보일러 플레이트 감소가 매우 간단한 경우 (한 인스턴스 당 하나의 클래스 및 이와 유사한 경우), 때로는 적절한 서비스 객체를 선택하는 것이 싱글 톤 개체에 클래스를 매핑하는 것만 큼 쉽지 않다는 것입니다.

의존성 주입 방법을 의미합니다 (실제로 그래도) 코드의 한 부분 (예 : 클래스)의 경우, 하드 코딩되지 않은 상태에서 의존성 (예 : 다른 클래스의 다른 부분, 다른 클래스, 다른 클래스에 따라 다름)에 액세스 할 수 있도록 (예 : 자유롭게 또는 자유롭게 변경하거나 재정의 할 수 있습니다. 필요에 따라 다른 시간에로드 됨)

(및 PS, 예, 다소 단순하고 개념으로 지나치게 과장된 25 $ 이름이되었습니다), 나의 .25 센트

이미 많은 답이 있다는 것을 알고 있지만 이것이 매우 도움이된다는 것을 알았습니다. http://tutorials.jenkov.com/dependency-injection/index.html

의존성 없음 :

public class MyDao {

  protected DataSource dataSource =
    new DataSourceImpl("driver", "url", "user", "password");

  //data access methods...
  public Person readPerson(int primaryKey) {...}

}

의존:

public class MyDao {

  protected DataSource dataSource = null;

  public MyDao(String driver, String url, String user, String
 password){
    this.dataSource = new DataSourceImpl(driver, url, user, password);
  }

  //data access methods...
  public Person readPerson(int primaryKey)
  {...}

}

어떻게 DataSourceImpl 인스턴스화는 생성자로 이동됩니다. 생성자는 4 개의 매개 변수를 취합니다. DataSourceImpl. 그러나 MyDao 클래스는 여전히이 네 가지 값에 의존하며 더 이상 이러한 종속성 자체를 만족시키지 않습니다. 그들은 어떤 클래스를 창조하든 제공합니다 MyDao 사례.

인기있는 답변은 유용하지 않은 방식으로 의존성 주입을 정의하기 때문에 도움이되지 않습니다. "종속성"을 통해 우리는 객체 X가 필요로하는 기존의 다른 객체를 의미한다는 데 동의합시다. 하지만 우리는 우리가 말할 때 우리가 "의존성 주입"을하고 있다고 말하지 않습니다.

$foo = Foo->new($bar);

우리는 그 전달 매개 변수를 생성자로 호출합니다. 우리는 생성자가 발명 된 이후로 정기적으로 그렇게 해왔습니다.

"의존성 주입"은 "제어의 역전"유형으로 간주되며, 이는 일부 논리가 발신자에서 꺼내 져야 함을 의미합니다. 발신자가 매개 변수를 통과 할 때는 그렇지 않으므로 DI라면 DI가 제어의 역전을 암시하지 않을 것입니다.

DI는 호출자와 생성자 사이에 의존성을 관리하는 중간 레벨이 있음을 의미합니다. Makefile은 의존성 주입의 간단한 예입니다. "발신자"는 명령 줄에 "Make Bar"를 입력하는 사람이고 "생성자"는 컴파일러입니다. makefile은 막대가 foo에 의존하도록 지정하고

gcc -c foo.cpp; gcc -c bar.cpp

하기 전에

gcc foo.o bar.o -o bar

"Make Bar"를 입력하는 사람은 막대가 FOO에 의존한다는 것을 알 필요가 없습니다. "Make Bar"와 GCC 사이에 의존성이 주입되었습니다.

중간 레벨의 주요 목적은 의존성을 생성자에게 전달하는 것이 아니라 모든 종속성을 나열하는 것입니다. 단지 한 곳, 코더에서 그들을 숨기기 위해 (코더가 그들을 제공하지 않도록).

일반적으로 중간 레벨은 구성된 물체에 대한 공장을 제공하며, 이는 요청 된 각 객체 유형이 만족 해야하는 역할을 제공해야합니다. 건설의 세부 사항을 숨기는 중간 수준을 가짐으로써 이미 공장에서 부과 한 추상화 페널티를 발생 시켰으므로 공장을 사용할 수도 있기 때문입니다.

의존성 주입은 일반적으로 "종속성 난독 화"요구 사항이라고 할 수있는 것에 대한 가능한 해결책 중 하나입니다. 의존성 난독 화는 클래스에 의존성을 제공하는 과정에서 '명백한'특성을 가져 오는 방법으로, 그에 따라 상기 클래스에 대한 의존성을 제공하는 클래스에 의존성을 제공하는 과정입니다. 이것이 반드시 나쁜 것은 아닙니다. 실제로, 클래스에 의존성이 제공되는 방식을 난독 화함으로써 클래스 외부의 무언가는 의존성을 만들어내는 일을 담당합니다. 즉, 다양한 시나리오에서는 다른 시나리오에서 다른 의존성 구현을 변경하지 않고 클래스에 제공 할 수 있습니다. 수업에. 이는 생산 및 테스트 모드 (예 : 'Mock'서비스 종속성 사용)를 전환하는 데 좋습니다.

불행히도 나쁜 부분은 일부 사람들이 의존성 난독 화를 수행하기 위해 전문화 된 프레임 워크가 필요하다고 가정했으며 특정 프레임 워크를 사용하지 않기로 선택한 경우 어떻게 든 '더 적은'프로그래머라고 생각한다는 것입니다. 많은 사람들이 믿는 또 다른 매우 혼란스러운 신화는 의존성 주입이 의존성 난독 화를 달성하는 유일한 방법이라는 것입니다. 이것은 명백하고 역사적으로 그리고 분명히 100% 잘못이지만 일부 사람들에게 의존성 난독 화 요구 사항에 대한 의존성 주입에 대한 대안이 있음을 설득하는 데 어려움을 겪을 것입니다.

프로그래머들은 수년간 의존성 난독 화 요구 사항을 이해해 왔으며 의존성 주입 전후에 많은 대체 솔루션이 진화했습니다. 공장 패턴이 있지만 특정 인스턴스에 대한 주입이 필요하지 않은 ThreadLocal을 사용하는 많은 옵션이 있습니다. 종속성은 실질적으로 스레드에 주입됩니다. 어느 클래스를 필요로하는 클래스에 주석을 추가하지 않고도 필요한 클래스를 요구하고 복잡한 XML '접착제'를 설정하여이를 가능하게합니다. 지속성 (JPA/JDO 등)에 의존성이 필요한 경우, 'Tranaparent 지속성'을 훨씬 쉽게 달성 할 수 있으며 Pojos로 구성된 도메인 모델 및 비즈니스 모델 클래스 (즉, 주석에 특정/고정되지 않은 프레임 워크).

책에서 '잘 근거가있는 Java 개발자 : Java 7 및 Polyglot 프로그래밍의 중요한 기술

DI는 특정 형태의 IOC로, 의존성을 찾는 프로세스는 현재 실행중인 코드를 직접 제어 할 수 없습니다.

책에서 Apress.spring.persistence.with.hibernate.oct.2010

종속성 주입의 목적은 응용 프로그램 비즈니스 로직에서 외부 소프트웨어 구성 요소를 해결하는 작업을 분리하는 것입니다. 종속성 주입없이 필요한 구성 요소가 필요한 서비스에 액세스하는 방법에 대한 세부 사항은 구성 요소의 코드와 함께 혼란스러워 질 수 있습니다. 이것은 오류의 가능성을 증가시키고 코드 부풀어 오르고 유지 보수 복잡성을 확대합니다. 그것은 구성 요소를 더 밀접하게 연결하므로 리팩토링 또는 테스트 할 때 종속성을 수정하기가 어렵습니다.

DI(종속성 주입)는 OOP의 기본 기능, 즉 한 객체와 다른 객체의 관계를 사용하는 디자인 패턴 중 하나입니다.상속은 한 개체를 상속하여 더 복잡하고 구체적인 다른 개체를 수행하는 반면, 관계 또는 연관은 단순히 속성을 사용하여 한 개체에서 다른 개체에 대한 포인터를 만듭니다.DI의 강력한 기능은 인터페이스 및 숨김 코드와 마찬가지로 OOP의 다른 기능과 결합됩니다.단순화를 위해 도서관에 한 권의 책만 빌릴 수 있는 고객(구독자)이 있다고 가정해 보겠습니다.

책의 인터페이스:

package com.deepam.hidden;

public interface BookInterface {

public BookInterface setHeight(int height);
public BookInterface setPages(int pages);   
public int getHeight();
public int getPages();  

public String toString();
}

다음으로 우리는 많은 종류의 책을 가질 수 있습니다.유형 중 하나는 허구입니다.

package com.deepam.hidden;

public class FictionBook implements BookInterface {
int height = 0; // height in cm
int pages = 0; // number of pages

/** constructor */
public FictionBook() {
    // TODO Auto-generated constructor stub
}

@Override
public FictionBook setHeight(int height) {
  this.height = height;
  return this;
}

@Override
public FictionBook setPages(int pages) {
  this.pages = pages;
  return this;      
}

@Override
public int getHeight() {
    // TODO Auto-generated method stub
    return height;
}

@Override
public int getPages() {
    // TODO Auto-generated method stub
    return pages;
}

@Override
public String toString(){
    return ("height: " + height + ", " + "pages: " + pages);
}
}

이제 구독자는 책에 연결할 수 있습니다.

package com.deepam.hidden;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class Subscriber {
BookInterface book;

/** constructor*/
public Subscriber() {
    // TODO Auto-generated constructor stub
}

// injection I
public void setBook(BookInterface book) {
    this.book = book;
}

// injection II
public BookInterface setBook(String bookName) {
    try {
        Class<?> cl = Class.forName(bookName);
        Constructor<?> constructor = cl.getConstructor(); // use it for parameters in constructor
        BookInterface book = (BookInterface) constructor.newInstance();
        //book = (BookInterface) Class.forName(bookName).newInstance();
    } catch (InstantiationException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    } catch (NoSuchMethodException e) {
        e.printStackTrace();
    } catch (SecurityException e) {
        e.printStackTrace();
    } catch (IllegalArgumentException e) {
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        e.printStackTrace();
    }
    return book;
}

public BookInterface getBook() {
  return book;
}

public static void main(String[] args) {

}

}

자체 구현을 위해 세 가지 클래스를 모두 숨길 수 있습니다.이제 DI에 이 코드를 사용할 수 있습니다.

package com.deepam.implement;

import com.deepam.hidden.Subscriber;
import com.deepam.hidden.FictionBook;

public class CallHiddenImplBook {

public CallHiddenImplBook() {
    // TODO Auto-generated constructor stub
}

public void doIt() {
    Subscriber ab = new Subscriber();

    // injection I
    FictionBook bookI = new FictionBook();
    bookI.setHeight(30); // cm
    bookI.setPages(250);
    ab.setBook(bookI); // inject
    System.out.println("injection I " + ab.getBook().toString());

    // injection II
    FictionBook bookII = ((FictionBook) ab.setBook("com.deepam.hidden.FictionBook")).setHeight(5).setPages(108); // inject and set
    System.out.println("injection II " + ab.getBook().toString());      
}

public static void main(String[] args) {
    CallHiddenImplBook kh = new CallHiddenImplBook();
    kh.doIt();
}
}

의존성 주입을 사용하는 방법에는 여러 가지가 있습니다.싱글톤 등과의 결합도 가능하지만 여전히 기본적으로는 다른 객체 내부에 객체 형태의 속성을 생성하여 연관을 구현하는 것일 뿐입니다.유용성은 우리가 계속해서 작성해야 하는 코드가 항상 우리를 위해 준비되고 수행된다는 기능에만 있습니다.이것이 DI가 IoC(Inversion of Control)와 밀접하게 결합된 이유입니다. 즉, 우리 프로그램이 코드에 빈을 주입하는 다른 실행 모듈에 대한 제어를 전달한다는 의미입니다.(주입될 수 있는 각 객체는 서명되거나 Bean으로 간주될 수 있습니다.) 예를 들어 Spring에서는 생성 및 초기화를 통해 수행됩니다. 애플리케이션컨텍스트 이 작업을 수행하는 컨테이너입니다.우리는 코드에서 컨텍스트를 생성하고 빈 초기화를 호출합니다.그 순간 주입이 자동으로 수행되었습니다.

간단한 단어의 종속성 주입 (DI)은 다른 물체 사이의 종속성 또는 단단한 커플 링을 제거하는 방법입니다. 의존성 주입은 각 물체에 응집력있는 행동을 제공합니다.

DI는 Spring의 IOC 교장의 구현으로 "우리에게 전화하지 마십시오. 종속성 주입 프로그래머를 사용하면 새 키워드를 사용하여 객체를 만들 필요가 없습니다.

객체는 한 번 스프링 컨테이너에로드 된 다음 GetBean (String Beanname) 메소드를 사용하여 스프링 컨테이너에서 해당 객체를 가져와 필요할 때마다 재사용합니다.

의존성 주입은 스프링 프레임 워크와 관련된 개념의 핵심이며, 모든 프로젝트 스프링의 프레임 워크를 만들면 중요한 역할을 수행 할 수 있으며 여기에는 의존성 주입이 투수에 나옵니다.

실제로 Java에서 클래스 A 및 클래스 B로 두 개의 다른 클래스를 만들었고 클래스 B에서 사용할 수있는 기능이 무엇이든 클래스 A에서 사용할 수 있으므로 그때 의존성 주입을 사용할 수 있습니다. 한 클래스의 개체를 다른 클래스의 상자를 상자로 만들 수있는 경우, 같은 방식으로 다른 클래스에서 전체 클래스를 주입하여 액세스 할 수 있도록 할 수 있습니다. 이런 식으로 의존성을 극복 할 수 있습니다.

의존성 주입은 단순히 두 클래스를 붙이고 동시에 분리 된 상태로 유지하는 것입니다.

의존성 주입 (DI)은 의존성 반전 원리 (DIP) 실습의 일부이며, 이는 IOC (Control) 반전이라고도합니다. 기본적으로 하나의 모 놀리 식 시스템 대신 코드를보다 모듈 식 및 단위 테스트 가능하게 만들려면 DIP를 수행해야합니다. 따라서 클래스에서 분리되어 추상화 될 수있는 코드의 일부를 식별하기 시작합니다. 이제 추상화의 구현은 클래스 외부에서 주입되어야합니다. 일반적으로 이것은 생성자를 통해 수행 할 수 있습니다. 따라서 추상화를 매개 변수로 받아들이는 생성자를 만듭니다.이를 생성자를 통해 의존성 주입이라고합니다. DIP, DI 및 IOC 컨테이너에 대한 자세한 내용은 읽을 수 있습니다. 여기

나는 의존성 주입이 무엇인지, 주요 목표에 초점을 맞추는 것에 대한 약간 다르고 짧고 정확한 정의를 제안합니다. 여기):

종속성 주입은 각 서비스가 종속성에 의해 매개 변수로 표시되는 서비스 객체의 정적 상태의 상태 그래프를 생성하는 프로세스입니다.

우리가 응용 프로그램에서 만든 객체 (Java, C# 또는 기타 객체 지향 언어를 사용하든 관계없이)는 일반적으로 무국적, 정적 및 글로벌 "서비스 객체"(모듈), 상태, 역동적 및 로컬의 두 가지 범주 중 하나에 속합니다. "데이터 객체".

서비스 개체의 그래프 인 모듈 그래프는 일반적으로 응용 프로그램 시작에서 작성됩니다. 이것은 스프링과 같은 컨테이너를 사용하여 수행 할 수 있지만 매개 변수를 객체 생성자에게 전달하여 수동으로 수행 할 수도 있습니다. 두 가지 방법 모두 장단점을 가지고 있지만 응용 프로그램에서 DI를 사용하는 데 프레임 워크가 필요하지 않습니다.

한 가지 요구 사항은 서비스가 종속성에 의해 매개 변수를 매개해야한다는 것입니다. 이것이 의미하는 바는 주어진 시스템에서 취한 언어와 접근 방식에 달려 있습니다. 일반적으로 이것은 생성자 매개 변수의 형태를 취하지만 세터를 사용하는 것도 옵션입니다. 이는 또한 서비스 사용자로부터 서비스의 종속성이 (서비스 방법을 호출 할 때) 숨겨져 있음을 의미합니다.

언제 사용해야합니까? 응용 프로그램이 로직을 별도의 모듈로 캡슐화 할 수있을만큼 충분히 커질 때마다 모듈 간의 종속성 그래프는 코드의 가독성과 탐색 가능성을 얻을 수 있습니다.

의존성 주입 "의 구현 유형"입니다.제어의 역전"기반 프레임 워크 건물의 원리.

프레임 워크 GOF의 "디자인 패턴"에 언급 된 바와 같이, 개발자가이를 위해 개발자를 높이는 주요 제어 흐름 로직을 구현하는 클래스는 이런 식으로 프레임 워크가 제어 원칙의 역전을 실현합니다.

클래스 계층 구조가 아닌 기술로 구현하는 방법 인이 IOC 원칙은 의존성 주입 일뿐입니다.

DI 주로 클래스 인스턴스의 매핑을 위임하고 해당 인스턴스에 대한 참조를 유형으로, 외부 "엔티티"에 유형으로 유형으로 구성합니다. 객체, 정적 클래스, 구성 요소, 프레임 워크 등 ...

클래스 인스턴스는 "의존성", 참조를 통해 클래스 인스턴스와 호출 구성 요소의 외부 바인딩은주입".

분명히 OOP 관점에서 원하는 대로이 기술을 여러 가지 방법으로 구현할 수 있습니다. 예를 들어 참조하십시오. 생성자 주입, 세터 주입, 인터페이스 주입.

객체와의 일치 작업을 수행하기 위해 제 3자를 위임하면 동일한 서비스 구현에서 일부 서비스가 필요한 구성 요소를 완전히 분리하려는 경우 매우 유용합니다.

이러한 방식으로 구성 요소를 설계 할 때는 아키텍처 및 특정 논리에만 초점을 맞출 수 있으며 사용중인 동일한 개체의 구현 변경 사항에 대한 걱정없이 다른 개체와 협력하기위한 인터페이스를 신뢰할 수 있습니다. 완전히 교체됩니다 (분명히 인터페이스를 존중).

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top