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


3074

의존성 주입 에 대한 특정 질문 , 사용시기 및 프레임 워크와 같은 몇 가지 질문이 이미 게시되어 있습니다. 하나,

의존성 주입이란 무엇이며 언제 / 왜 사용해야합니까?


Dependency Injection Here 에 대한 내 토론을 참조하십시오 .
Kevin S.

33
링크에 관한 의견에 동의합니다. 다른 사람을 언급하고 싶을 수도 있습니다. 그러나 적어도 당신이 그것들을 연결하는 이유와 구글을 사용하여 얻을 수있는 다른 링크
Christian Payne

@AR : 기술적으로, Dependency Injection은 특별한 형태의 IoC 가 아닙니다 . 오히려 IoC는 의존성 주입을 제공하는 데 사용되는 기술 중 하나입니다. 의존성 주입을 제공하기 위해 다른 기술이 사용될 수 있지만 (IoC가 일반적으로 사용되는 유일한 기술 임에도 불구하고) IoC는 다른 많은 문제에도 사용됩니다.
Sean Reilly

제가 DI에 대해 읽은 가장 좋은 설명 중 하나는 Google의 Guice (주스로 발음)입니다. http://code.google.com/p/google-guice/wiki/Motivation?tm=6
Raj

136
링크와 관련하여 링크가 종종 사라지는 것을 기억하십시오. SO 답변에는 점점 더 많은 죽은 링크가 있습니다. 따라서 연결된 기사가 아무리 훌륭하더라도 찾을 수 없으면 전혀 좋지 않습니다.
DOK

답변:


1931

의존성 주입 은 다른 객체프레임 워크 (종속성 인젝터)에 의존성을 전달하고 있습니다.

의존성 주입은 테스트를 더 쉽게 만듭니다. 주입은 constructor을 통해 수행 할 수 있습니다 .

SomeClass() 생성자는 다음과 같습니다.

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

문제점 : myObject디스크 액세스 또는 네트워크 액세스와 같은 복잡한 작업 이 관련된 경우 장치 테스트를 수행 하기가 어렵 습니다 SomeClass(). 프로그래머는 모의해야 하며 팩토리 호출을 가로 챌myObject 수 있습니다 .

대체 솔루션 :

  • myObject생성자에 인수로 전달
public SomeClass (MyClass myObject) {
    this.myObject = myObject;
}

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

  • 일반적인 대안 중 하나는 do-nothing 생성자를 정의하는 입니다. 의존성 주입은 세터를 통해 수행 할 수 있습니다. (h / t @MikeVella).
  • Martin Fowler 는 세 번째 대안 (h / t @MarcDix)을 문서화했으며, 여기서 클래스 는 프로그래머가 주입하고자하는 종속성에 대한 인터페이스명시 적으로 구현합니다 .

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

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


25
Ben Hoffstein의 Martin Fowler의 기사에 대한 참조가 주제에 대한 '읽어야 함'을 가리키는 것으로 필요하다는 것을 인정하면 실제로 여기에 대한 질문에 대답하기 때문에 wds의 대답을 받아들입니다.
AR.

121
설명과 동기 부여를 위해 +1 : 클래스가 다른 사람의 문제에 의존하는 객체를 만드는 것 . 또 다른 방법은 DI가 수업의 응집력을 높이는 것입니다 (책임이 적음).
Fuhrmanator

13
당신은 의존성이 "생성자에게 전달된다"고 말하지만 이해하는 것처럼 이것은 엄격하게 사실이 아닙니다. 객체가 인스턴스화 된 후 종속성이 속성으로 설정되어 있으면 여전히 종속성 주입입니까?
Mike Vella

1
@MikeVella 예, 맞습니다. 속성은 일반적으로 약간 더 유연하지만 대부분의 경우 실제로 차이가 없습니다. 나는 그것을 지적하기 위해 텍스트를 약간 편집 할 것입니다.
wds

2
지금까지 내가 찾은 최고의 답변 중 하나이므로 개선에 관심이 있습니다. 의존성 주입의 세 번째 형태 인 Interface injection에 대한 설명이 없습니다 .
Marc Dix

2351

지금까지 찾은 최고의 정의는 James Shore 가 정의한 것입니다 .

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

마틴 파울러의 글 도 유용 할 수 있습니다.

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

종속성은 생성자 주입 또는 세터 주입과 같은 많은 방법으로 객체에 주입 될 수 있습니다. 특별한 의존성 주입 프레임 워크 (예 : Spring)를 사용하여 그렇게 할 수도 있지만 반드시 필요한 것은 아닙니다. 의존성 주입을 위해 이러한 프레임 워크가 필요하지 않습니다. 명시 적으로 객체 (종속성)를 인스턴스화하고 전달하는 것은 프레임 워크에 의한 주입만큼이나 좋은 주입입니다.


35
나는 제임스의 기사에 대한 설명, 특히 마지막 부분에 대한 설명이 마음에 듭니다. "여전히 세 가지 개념 ( 'TripPlanner', 'CabAgency'및 'AirlineAgency')을 취하는 모든 접근 방식에 감탄해야합니다. "한 줄의 응용 프로그램 논리를 작성하기 전에 수십 줄의 접착 코드와 구성 XML을 추가합니다." 이것은 내가 (슬프게도) 매우 자주 본 것입니다. 의존성 주입 (그가 설명한대로 그 자체로 좋은 것)은 "지원하는"코드를 작성하는 것을 끝내기 위해 더 쉽게 할 수있는 것들을 지나치게 복잡하게 만드는 데 잘못 사용됩니다.
Matt

2
Re : "객체 (종속성)를 명시 적으로 구현하고 전달하는 것은 프레임 워크에 의한 주입만큼이나 좋은 주입입니다." 사람들이 왜 그렇게 프레임 워크를 만들었 을까요?
dzieciou

13
같은 이유로 모든 프레임 워크가 작성 (또는 최소한 도착해야 함)되는 이유는 특정 복잡성에 도달하면 작성해야하는 반복 / 보일러 플레이트 코드가 많기 때문입니다. 문제는 사람들이 엄격하게 필요하지 않은 경우에도 프레임 워크에 도달하는 경우가 많다는 것입니다.
Thiago Arrais

14
5 센트짜리 개념에 대한 25 달러의 임기는 끝났다. 여기 도움이 된 좋은 기사가 있습니다 : codeproject.com/Articles/615139/…
Christine

@Matt 구성 파일은 "실제 런타임까지 결정을 연기합니다"라는 "결정 연기"입니다. 단검과 특히 단검은 제 생각에 "응용 프로그램 조립 시간까지 결정을 연기합니다"라는 장점을 발견했습니다.
Thorbjørn Ravn Andersen은

645

느슨한 커플 링 측면 에서이 재미있는 예를 찾았습니다 .

모든 응용 프로그램은 서로 협력하여 유용한 작업을 수행하는 많은 개체로 구성됩니다. 전통적으로 각 오브젝트는 협업하는 종속 오브젝트 (종속성)에 대한 자체 참조를 가져옵니다. 이것은 고도로 결합 된 클래스와 테스트하기 어려운 코드로 이어집니다.

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

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

의존성 주입이없는 경우 (DI) :

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

  //The rest
}

여기서 Car개체 는 종속 개체를 만드는 역할을합니다.

Wheel초기 NepaliRubberWheel()펑크 후 종속 객체의 유형을 변경하려면 어떻게해야 합니까? 새로운 의존성 인 say를 사용하여 Car 객체를 다시 만들어야 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;
  }
}

출처 : 의존성 주입 이해


20
내가 이것을 이해하는 방법은 다른 객체의 일부로 새 객체를 인스턴스화하는 대신, 필요할 때 필요한 경우 해당 객체를 주입하여 첫 번째 객체의 의존성을 제거하는 것입니다. 맞습니까?
JeliBeanMachine

나는 여기 커피 숍 예제와 함께 이것을 설명했다 : digigene.com/design-patterns/dependency-injection-coffeeshop
Ali Nem

11
간단한 비유를 사용하는 일반 영어이기 때문에이 비유를 좋아합니다. 평판 타이어 생산을 기존이있는 경우, 왜 타이어 제조 부문, 즉 수 있도록 처음부터 시작해야 내가이 (가), 조립 라인 굴러으로 디자인에서 차를 만들기에 너무 많은 재정적 지출과 인간 전력 이미 도요타 해요 말 new을 타이어? 난 아니야 내가해야 할 일은 그들로부터 (매개 변수를 통해 주입) 구매하고 설치하고 wah-lah하는 것입니다! 따라서 C # 프로젝트가 기존 라이브러리 / 클래스를 사용해야한다고 프로그래밍으로 돌아 오면, 전체 프로젝트에 대한 1 개의 참조를 실행 / 디버그하는 두 가지 방법이 있습니다
Jeb50

(계속), .. 외부 라이브러리 / 클래스 또는 DLL에서 2를 추가하십시오. 이 외부 클래스 내부에 무엇이 있는지 알 필요가 없다면 DLL로 추가하는 것이 더 쉬운 방법입니다. 따라서 옵션 1은 옵션 new이고 옵션 2는 매개 변수로 전달됩니다. 정확하지는 않지만 이해하기 쉬운 간단한 어리 석음.
Jeb50

1
@JeliBeanMachine (댓글에 대한 답글이 너무 늦어서 죄송합니다.) 휠 객체 또는 배터리 객체에 대한 첫 번째 객체의 종속성을 제거하는 것이 아니라 종속성을 전달하여 인스턴스 또는 구현을 구현할 수 있습니다. 의존. Before : Car는 NepaliRubberWheel에 하드 코딩 된 종속성이 있습니다. 이후 : Car는 Wheel 인스턴스에 의존성을 주입했습니다.
Mikael Ohlson

263

의존성 주입은 객체를 내부적으로 구성하는 대신 다른 코드 조각으로부터 객체의 인스턴스를 수신하는 방식으로 객체를 설계하는 방법입니다. 즉, 코드를 변경하지 않고도 객체에 필요한 인터페이스를 구현하는 모든 객체를 대체 할 수있어 테스트가 간소화되고 디커플링이 향상됩니다.

예를 들어 다음과 같은 걸쇠를 고려하십시오.

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::addManagerPersonService::removeManager의 인스턴스를해야 GroupMembershipService그 일을하기 위해. Dependency Injection이 없으면 기존의 생성 방식은 GroupMembershipService생성자에서 새로운 인스턴스를 생성하고 PersonService두 인스턴스 모두에서 해당 인스턴스 속성을 사용하는 것입니다. 그러나의 생성자 GroupMembershipService가 여러 항목을 필요로하거나 더 나쁜 경우에는에 대해 호출해야하는 초기화 "세터"가 GroupMembershipService있으며 코드가 빠르게 커지고 PersonService현재는 GroupMembershipService다른 것뿐만 아니라 GroupMembershipService에 달려 있습니다. 또한에 대한 링크 GroupMembershipService는 하드 코드되어 있으므로 PersonService"더미"할 수 없습니다.GroupMembershipService 테스트 목적으로 또는 응용 프로그램의 다른 부분에서 전략 패턴을 사용합니다.

Dependency Injection을 사용하면의 GroupMembershipService내부 를 인스턴스화하는 대신 생성자 PersonService에 전달 PersonService하거나 속성 (getter 및 setter)을 추가하여 로컬 인스턴스를 설정할 수 있습니다. 이것은 PersonService더 이상을 만드는 방법에 대해 걱정할 필요가 없으며 GroupMembershipService주어진 것을 수락하고 작동합니다. 이것은 또한의 서브 클래스입니다 아무 의미 GroupMembershipService, 또는 구현 GroupMembershipService인터페이스는에 "주입"할 수 있습니다를 PersonService하고,이 PersonService변화에 대해 알 필요가 없다.


29
DI를 사용하여 동일한 코드 예제를 제공 할 수 있다면 좋을 것입니다.
CodyBugstein

170

받아 들일만한 대답은 좋은 것입니다.하지만 DI는 코드에서 하드 코딩 된 상수를 피하는 고전과 매우 흡사합니다.

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

DI는 객체 지향 프로그래밍 세계에서 이와 유사합니다. 상수 리터럴 대신 값이 전체 객체이지만 클래스 코드에서 코드를 생성하는 이유는 비슷합니다. 객체가이를 사용하는 코드보다 자주 변경됩니다. 그러한 변경이 필요한 중요한 경우는 테스트입니다.


18
+1 "개체가 사용하는 코드보다 개체가 더 자주 변경됩니다". 일반화하려면 플럭스 지점에 간접 성을 추가하십시오. 플럭스 지점에 따라 간접 이름은 다른 이름으로 불립니다 !!
Chethan

139

Car 클래스 와 Engine 클래스 를 사용하여 간단한 예제를 시도해 봅시다 . 모든 자동차는 적어도 지금은 어디든 갈 수있는 엔진이 필요합니다. 아래 코드는 의존성 주입없이 어떻게 보일 것입니다.

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 car = new Car();

우리가 GasEngine에 밀접하게 결합 한이 코드의 문제는 그것을 ElectricityEngine으로 변경하기로 결정한 경우 Car 클래스를 다시 작성해야합니다. 그리고 응용 프로그램이 클수록 더 많은 문제와 두통이 발생하여 새로운 유형의 엔진을 추가하고 사용해야합니다.

다시 말해,이 접근 방식을 사용하면 고급 자동차 등급은 SOLID의 DIP (Dependency Inversion Principle)를 위반하는 하위 레벨 GasEngine 등급에 의존합니다. 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();
        }
    }

이제 Car 클래스는 엔진의 특정 구현이 아니라 IEngine 인터페이스에만 의존합니다. 이제 유일한 트릭은 어떻게 Car 인스턴스를 생성하고 GasEngine 또는 ElectricityEngine과 같은 실제 콘크리트 엔진 클래스를 제공 하는가입니다. 그것이 의존성 주입 이 들어오는 곳 입니다.

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

여기서 기본적으로 의존성 (Engine instance)을 Car 생성자에 주입 (전달)합니다. 이제 우리 클래스는 객체와 그 종속성 사이의 느슨한 결합을 가지고 있으며 Car 클래스를 변경하지 않고도 새로운 유형의 엔진을 쉽게 추가 할 수 있습니다.

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

결국 의존성 주입 은 객체와 그 의존성 사이의 느슨한 결합을 달성하는 기술 일뿐입니다. 클래스가 작업을 수행하기 위해 필요한 종속성을 직접 인스턴스화하지 않고 생성자 주입을 통해 클래스에 종속성을 제공합니다 (대부분의 경우).

또한 많은 의존성이있는 경우 컨트롤의 IoC (Inversion of Control) 컨테이너를 사용하는 것이 좋습니다.이 인터페이스를 사용하면 모든 인터페이스에 대해 어떤 구체적인 구현에 어떤 인터페이스를 매핑해야하는지 알 수 있으며 구성 할 때 해당 종속성을 해결할 수 있습니다 우리의 목표. 예를 들어, IoC 컨테이너의 매핑에서 IEngine 종속성이 GasEngine 클래스에 매핑되어야한다고 지정할 수 있으며 IoC 컨테이너에 Car 클래스 의 인스턴스를 요청하면 GasEngine 종속성으로 Car 클래스 가 자동으로 생성됩니다. 통과했다.

업데이트 : 최근 Julie Lerman의 EF Core에 대한 코스를 시청했으며 DI에 대한 짧은 정의를 좋아했습니다.

의존성 주입은 응용 프로그램이 필요한 클래스에 객체를 즉시 주입 할 수 있도록하는 패턴으로, 해당 클래스가 해당 객체를 책임지지 않도록합니다. 코드를 느슨하게 결합 할 수 있으며 Entity Framework Core는이 동일한 서비스 시스템에 플러그인됩니다.


2
호기심에서 전략 패턴과 다른 점은 무엇입니까? 이 패턴은 알고리즘을 캡슐화하여 상호 교환 가능하게 만듭니다. 의존성 주입과 같은 느낌과 전략 패턴은 매우 유사합니다.
elixir

110

낚시하러 가고 싶다고 상상해 봅시다.

  • 의존성 주입이 없으면 모든 것을 스스로 관리해야합니다. 보트를 찾고, 낚싯대를 구입하고, 미끼 등을 찾는 것이 필요합니다. 물론 가능하지만, 그것은 당신에게 많은 책임이 있습니다. 소프트웨어 용어로,이 모든 것을 검색해야합니다.

  • 의존성 주입을 사용하면 다른 사람이 모든 준비를 처리하고 필요한 장비를 사용할 수 있습니다. 보트, 낚싯대 및 미끼를 모두 사용할 수 있습니다.


59
단점은, 화장실을 다시 만들기 위해 배관공을 고용했다고 가정 해 봅시다. 그러면 그는 "좋아요, 여기 제가 당신을 위해 필요한 도구와 재료의 목록이 있습니다"라고 말합니다. 그게 배관공의 일이 아니어야합니까?
jscs

그래서 누군가가 알지 못하는 사람을 돌봐야 할 필요가있다. 그러나 아직 사용할 준비가되었지만 보트, 스틱 및 미끼 목록을 수집하기로 결정한다.
Chookoos

22
@JoshCaswell 아니, 배관공의 빈 자리가 될 것입니다. 고객으로서 배관 공사가 필요합니다. 이를 위해서는 배관공이 필요합니다. 배관공은 도구가 필요합니다. 이를 얻기 위해 배관 회사가 장비를 갖추게됩니다. 고객은 배관공이 무엇을해야하는지 정확히 알고 싶지 않습니다. 배관공은 필요한 것을 알고 있지만 모든 것을 얻는 것이 아니라 일을하고 싶습니다. 배관공 고용주로서 귀하는 배관공을 사람들의 집으로 보내기 전에 필요한 것을 갖추어야합니다.
sara

@kai 당신의 요점을 이해합니다. 소프트웨어에서 우리는 공장에 대해 이야기하고 있습니다. 맞습니까? 그러나 DI는 일반적으로 클래스가 여전히 주입되지 않으므로 팩토리를 사용하지 않음을 의미합니다. 고객은 도구를 제공하기 위해 고용주 (공장)에 연락하여 배관공에게 전달할 수 있어야합니다. 실제로 프로그램에서 어떻게 작동하지 않습니까? 따라서 고객 (클래스 / 기능 / 무엇을 부르든)이 도구를 조달 할 필요는 없지만, 여전히 고용주 (공장)에서 배관공 (주입 된 클래스)에게 도구를 제공하는 중개인이어야합니다.
KingOfAllTrades

1
@KingOfAllTrades : 물론 어느 시점에는 배관공을 고용하고 옷을 입는 사람이 있거나 배관공이 없습니다. 그러나 고객은 그렇게하지 않습니다. 고객은 배관공을 요구하고 이미 일을하는 데 필요한 것을 갖추 었습니다. DI를 사용하면 결국 종속성을 충족시키는 코드가 있습니다. 그러나 실제 작동하는 코드와는 분리되어 있습니다. 최대한 활용하면 객체가 종속성을 알 수 있으며 객체 그래프 작성은 종종 init 코드에서 외부에서 발생합니다.
cHao

102

이것은 내가 본 의존성 주입의존성 주입 컨테이너 에 대한 가장 간단한 설명입니다 .

의존성 주입없이

  • 응용 프로그램에는 Foo (예 : 컨트롤러)가 필요합니다.
  • 응용 프로그램이 Foo를 만듭니다
  • 응용 프로그램 호출 Foo
    • Foo에는 Bar (예 : 서비스)가 필요합니다.
    • 푸는 바를 만듭니다
    • 푸 콜 바
      • Bar는 Bim (서비스, 저장소 등)이 필요하므로 다음과 같이하십시오.
      • 바는 Bim을 만듭니다
      • 바 뭔가를

의존성 주입

  • 응용 프로그램에는 Boo가 필요한 Foo가 필요하고 Bim이 필요합니다.
  • 응용 프로그램이 Bim을 만듭니다
  • 응용 프로그램은 막대를 만들어 Bim을 제공합니다.
  • 응용 프로그램이 Foo를 작성하고이를 제공합니다.
  • 응용 프로그램 호출 Foo
    • 푸 콜 바
      • 바 뭔가를

의존성 주입 컨테이너 사용

  • 응용 프로그램은 Foo가 필요합니다.
  • 응용 프로그램은 컨테이너에서 Foo를 가져옵니다.
    • 컨테이너가 Bim을 생성
    • 컨테이너가 Bar를 생성하고 Bim에게 제공
    • 컨테이너가 Foo를 생성하고 Bar를 제공합니다
  • 응용 프로그램 호출 Foo
    • 푸 콜 바
      • 바 뭔가를

의존성 주입의존성 주입 컨테이너 는 서로 다릅니다.

  • 의존성 주입은 더 나은 코드를 작성하는 방법입니다
  • DI 컨테이너는 의존성 주입을 돕는 도구입니다

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



55

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

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 버전에서는 인수없는 생성자에서 myDatabase 객체를 초기화하고 싶지 않습니까? 오버로드 된 생성자를 호출하지 않고 DoStuff를 호출하려고하면 아무런 의미가 없으며 예외가 발생합니까?
Matt Wilko

new DatabaseThingie()유효한 myDatabase 인스턴스를 생성하지 않는 경우에만 .
JaneGoodall

40

Dependency Injection 개념을 이해하기 쉽게 만듭니다. 전구를 토글 (켜기 / 끄기)하기위한 스위치 버튼을 예로 들어 보겠습니다.

의존성 주입없이

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

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

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

의존성 주입

Switch는 나에게 Bulb가 전달 될 때마다 켜거나 끌 필요가 있다는 것을 알고 있습니다. 그래서,

스위치-> Bulb1 OR Bulb2 OR NightBulb (주사 된 의존성)

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

스위치 및 전구에 대한 James 예제 수정 :

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(); 
    ... 
  } 
}`

36

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

다른 사람들이 말했듯이, Dependency Injection (DI) 은 우리의 관심 클래스 (소비자 클래스)가 의존하는 ( UML 의미에서 ) 다른 객체 인스턴스의 직접 생성 및 수명 관리에 대한 책임을 제거합니다 . 이러한 인스턴스는 일반적으로 생성자 매개 변수 또는 속성 설정자를 통해 소비자 클래스로 전달됩니다 (종속성 개체 인스턴스화 및 소비자 클래스로 전달은 일반적으로 IoC (Inversion of Control) 컨테이너에서 수행하지만 다른 주제입니다) .

DI, DIP 및 SOLID

특히, 로버트 C 마틴의 패러다임에 디자인을 지향 객체의 SOLID 원칙 , DI의 가능한 구현 중 하나 인 의존 관계 역전 원칙 (DIP) . DIP는이다 DSOLID진언이 다른 DIP 구현은 서비스 로케이터 및 플러그인 패턴을 포함 -.

딥의 목적은 클래스 사이 꽉 콘크리트 종속성을 분리하고, 대신에,를 통해 달성 될 수 추상화에 의해서 결합을 느슨하게하는 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의 한 가지 결과는 종속성 개체가 이제 생성자 또는 setter 주입을 통해 소비 클래스로 전달되므로 종속성 개체 인스턴스의 수명 관리가 더 이상 소비 클래스에 의해 제어되지 않는다는 것입니다.

이것은 다른 방법으로 볼 수 있습니다 :

  • 소비 클래스에 의한 종속성의 수명 제어를 유지해야하는 경우 종속성 클래스 인스턴스를 작성하기위한 (추상) 팩토리를 소비자 클래스에 주입하여 제어를 다시 설정할 수 있습니다. 소비자는 Create필요에 따라 공장에서 인스턴스를 통해 인스턴스를 얻을 수 있으며 완료되면 이러한 인스턴스를 폐기 할 수 있습니다.
  • 또는 종속성 인스턴스의 수명 제어를 IoC 컨테이너에 양도 할 수 있습니다 (자세한 내용은 아래 참조).

DI는 언제 사용합니까?

  • 동등한 구현을 위해 종속성을 대체해야 할 가능성이있는 경우,
  • 의존성을 분리하여 클래스의 메소드를 단위 테스트해야 할 때마다,
  • 의존성 수명의 불확실성이 실험을 보장 할 수있는 곳 (예 : Hey, 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; }
}

또한 Consolea와 같은 추상화 에 대한 종속성을 완화 할 수 있습니다 TextWriter. 의존성 주입은 일반적으로 constructor주입 (소비에 대한 추상화를 소비 클래스의 생성자에 매개 변수로 Setter Injection전달 ) 또는 ( setXyz()세터 또는 {set;}정의 된 .Net 속성을 통해 종속성 전달 )으로 구현됩니다. 생성자 클래스는 생성 후 클래스가 올바른 상태가되고 내부 종속성 필드가 readonly(C #) 또는 final(Java) 로 표시 될 수 있으므로 생성자 주입이 선호됩니다 . 위의 예제에서 생성자 주입을 사용하면 다음과 같이 남습니다.

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 컨테이너에 의해 두 개의 종속성이 제공되어야 함)

자동화 된 단위 테스트 (Unit Test)를 구축 할 수 있는데, 이는 이제 의존성-시간을 제어 할 수 있고 기록 된 결과를 감시 할 수 있기 때문에 로거가 올바르게 작동하고 있음을 확실히 증명합니다.

[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());
}

다음 단계

의존성 주입은 제어 컨테이너 (IoC)반전 과 항상 연관되어 있으며 , 구체적인 의존성 인스턴스를 주입 (제공)하고 수명 인스턴스를 관리합니다. 구성 / 부트 스트랩 프로세스 동안 IoC컨테이너는 다음을 정의 할 수 있습니다.

  • 각 추상화와 구성된 구체적인 구현 간의 매핑 (예 : "소비자가 요청하면 인스턴스를 IBar반환 할 때마다 ConcreteBar" )
  • 정책은 각 종속 인스턴스의 수명 관리를 위해 설정 될 수 있습니다 (예 : 각 소비자 인스턴스에 대한 새 객체 생성, 모든 소비자간에 싱글 톤 종속성 인스턴스 공유, 동일한 스레드에서만 동일한 종속성 인스턴스 공유 등).
  • .Net에서 IoC 컨테이너는 구성된 수명 관리 IDisposableDisposing따라 종속성 과 같은 프로토콜을 인식 하고 종속성을 담당 합니다.

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

DI 친화적 인 코드의 핵심은 클래스의 정적 결합을 피하고 의존성 작성에 new ()를 사용하지 않는 것입니다.

위의 예에 따라 종속성의 디커플링에는 약간의 디자인 노력이 필요하며 개발자에게는 new종속성을 직접 습관화 하는 대신 패러다임 전환이 필요 하며 대신 컨테이너를 신뢰하여 종속성을 관리해야합니다.

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

참고 : new ..()POCO / POJO / Serialization DTOs / Entity Graphs / Anonymous JSON 투영 등 의 생성 / 매핑 / 투영 ( 예 : "데이터 전용"클래스 또는 레코드)-메소드에서 사용되거나 반환 된 것은 종속성으로 간주 되지 않습니다 . UML 감각) 및 DI의 영향을받지 않습니다. new이것들을 투영하는 데 사용하면 됩니다.


1
문제는 DIP! = DI입니다. DIP는 구현과 추상화를 분리하는 것에 관한 것입니다. A. 상위 모듈은 하위 모듈에 의존해서는 안됩니다. 둘 다 추상화에 의존해야합니다. B. 추상화는 세부 사항에 의존해서는 안됩니다. 세부 사항은 추상화에 따라 달라집니다. DI는 객체 생성과 객체 사용을 분리하는 방법입니다.
Ricardo Rivaldo

그렇습니다. 구별은 Bob 3의 SOLID 패러다임에서 제 2 항 "DIP의 가능한 구현 중 하나 인 DI" 에 명확하게 설명되어 있습니다. 나는 또한 이전 게시물에서 이것을 분명히 했다.
StuartLC

25

DI (Dependency Injection)의 핵심은 응용 프로그램 소스 코드를 깨끗 하고 안정적으로 유지하는 것입니다 .

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

실제로 모든 디자인 패턴은 향후 변경 사항이 최소 파일에 영향을 미치도록 우려를 분리합니다.

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

예 : 쉘 스크립트가있는 DI

Java 외부에서 가끔 일하는 경우, source많은 스크립팅 언어 (Shell, 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"

이제 문제는 종속적 인 "component"가 초기화 자체를 수행해야한다는 것입니다.

종속성의 초기화가 변경 될 때마다 "컴포넌트"의 소스 코드 파일에 대한 새로운 릴리스가 필요하기 때문에 "컴포넌트"의 소스 코드는 깨끗 하거나 안정적이지 않습니다 .

마지막 말

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

그러나 다음과 같은 우려를 나누기위한 일반적인 접근 방식입니다.

  • 응용 프로그램 개발 ( 단일 소스 코드 릴리스 수명주기)
  • 응용 프로그램 배포 ( 독립적 인 수명주기를 가진 여러 대상 환경)

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


DI의 목적으로 종속성을 완료하지 않고도 특정 클래스 (테스트)를 완료하는 기능을 추가합니다.
David

22

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

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

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

20 세기 중반 릴 투 릴 휴대용 테이프 레코더.

위 이미지는 20 세기 중반 Reel-to-Reel 휴대용 테이프 레코더의 이미지입니다. 소스 .

테이프 레코더 기계의 주요 목적은 사운드를 녹음하거나 재생하는 것입니다.

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

  1. 기계 내부에 릴을 놓을 수 있습니다
  2. 릴을 놓을 수있는 고리를 제공 할 수 있습니다.

첫 번째를 사용하는 경우 릴을 변경하려면 기계를 열어야합니다. 우리가 두 번째 것을 선택한다면, 그것은 릴을위한 고리를 놓는 것입니다. 우리는 릴을 바꿔서 음악을 연주 할 수있는 추가적인 이점을 얻고 있습니다. 또한 릴에있는 것만 재생하는 기능을 줄입니다.

현명한 의존성 주입은 의존성을 외부화하여 구성 요소의 특정 기능에만 중점을 두어 독립적 인 구성 요소를 결합하여 복잡한 시스템을 형성 할 수있는 프로세스입니다.

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

  • 높은 응집력과 느슨한 결합.
  • 의존성을 외부화하고 책임 만 고려합니다.
  • 구성 요소로 물건을 만들고 결합하여 고성능의 대형 시스템을 형성합니다.
  • 독립적으로 개발되어 적절히 테스트되었으므로 고품질 구성 요소를 개발하는 데 도움이됩니다.
  • 구성 요소가 고장 나면 구성 요소를 다른 것으로 교체하는 데 도움이됩니다.

오늘날 이러한 개념은 프로그래밍 세계에서 잘 알려진 프레임 워크의 기초를 형성합니다. Spring Angular 등은이 개념 위에 구축 된 잘 알려진 소프트웨어 프레임 워크입니다.

의존성 주입은 컴파일 타임에 어떤 클래스가 해당 기능을 제공하는 데 사용되는지 또는 단순히 속성을 객체에 주입하는 방법을 알지 않고도 다른 객체가 의존하는 객체의 인스턴스를 만드는 데 사용되는 패턴을 의존성 주입이라고합니다.

의존성 주입의 예

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

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();
}

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

제어 역전 및 의존성 주입의 차이점


17

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

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

Spring에서 Dependency Injection이 작동하는 방식 :

new 키워드를 사용하여 오브젝트를 하드 코딩 할 필요는 없으며 구성 파일에서 Bean 종속성을 정의하십시오. 스프링 컨테이너는 모든 연결을 담당합니다.

제어 역전 (IOC)

IOC는 일반적인 개념이며 다양한 방식으로 표현 될 수 있으며 Dependency Injection은 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는 bean을 인스턴스화하기 위해 인수가없는 생성자 또는 인수가없는 정적 팩토리 메소드를 호출 한 후 Bean에서 컨테이너의 setter 메소드를 호출하여 수행됩니다.

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 어노테이션에 기반한 어노테이션을 사용하는 경우 세터를 필수 종속성으로 만드는 데 사용할 수 있습니다.


15

내가 생각할 수있는 가장 좋은 비유는 수술실에서 외과의와 그의 조수입니다. 외과의는 주된 사람이자 외과의가 그에게 집중할 수 있도록 필요할 때 다양한 외과 용 구성 요소를 제공하는 조수입니다. 그가 최선을 다하는 것 (외과). 보조자가 없으면 의사는 필요할 때마다 구성 요소를 직접 가져와야합니다.

간단히 말해 DI는 종속 구성 요소를 제공하여 구성 요소에 대한 일반적인 추가 책임 (부담)을 제거하는 기술입니다.

DI는 다음과 같은 SR (Single Responsibility) 원칙에 더 가깝습니다 surgeon who can concentrate on surgery.

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

이유 : 코드를 쉽게 테스트하고 조롱 할 수 있기 때문에 변경 사항을 신속하게 테스트하여 시장에 출시 할 수 있습니다. 게다가 더 많은 제어가 가능한 코드베이스로의 여행을 지원하는 멋진 무료 도구 / 프레임 워크가 많을 때 왜 그렇지 않겠습니까?


@WindRider 감사합니다. 더 동의 할 수 없습니다. 인간의 삶과 인체는 디자인 우수성의 웅장한 예입니다 .. 척추는 ESB의 훌륭한 예입니다
:)

15

예를 들어 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생성자를 변경할 때 좋지 않습니다 . 이를 방지하기 위해
1) 간단한 매뉴얼 과 같은 DI 인젝터를 사용할 수 있습니다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, 당신은 단지 인젝터 클래스를 변경해야
  • use를 사용 Constructor Injection하면의 생성자를 보면 클래스의 Client종속성이 몇 개인 지 알 수 있습니다.Client

단점

  • use 사용 Constructor Injection하면 Service객체가 생성 될 때 Client생성되며 때로는 사용 Client하지 않고 클래스 에서 함수를 사용 Service하므로 생성 된 Service낭비

의존성 주입 정의

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

종속성 (사용할 수있는 객체이다 Service)
사출 종속성의 통과 (인 Service종속 객체) ( Client)을 사용할 것이라고


13

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

"종속 주입"(DI)은 "제어 역전"(IoC)으로도 알려져 있으며,이 느슨한 결합을 장려하는 기술로 사용될 수 있습니다.

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

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

생성자 주입

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

생성자는 구체적인 객체가 아닌 인터페이스를 허용합니다. 또한 orderDao 매개 변수가 널인 경우 예외가 발생합니다. 이것은 유효한 의존성을받는 것의 중요성을 강조합니다. 제 생각에는 생성자 주입은 객체에 종속성을 부여하는 데 선호되는 메커니즘입니다. 적절한 실행을 위해“Person”객체에 종속되어야하는 객체를 호출하는 동안 개발자에게 분명합니다.

세터 주입

그러나 다음 예제를 고려해보십시오. 종속성이없는 10 개의 메소드가있는 클래스가 있지만 IDAO에 종속 된 새 메소드를 추가한다고 가정하십시오. 생성자 생성자를 사용하도록 생성자를 변경할 수 있지만 이로 인해 모든 곳에서 모든 생성자 호출을 변경해야 할 수 있습니다. 또는 의존성을 취하는 새로운 생성자를 추가 할 수 있지만 개발자는 한 생성자를 다른 생성자보다 언제 사용 해야하는지 쉽게 알 수 있습니다. 마지막으로, 종속성이 작성하는 데 비용이 많이 드는 경우, 드물게 사용될 수있을 때 작성하여 작성해야하는 이유는 무엇입니까? "세터 주입"은 이와 같은 상황에서 사용할 수있는 또 다른 DI 기술입니다.

Setter Injection은 종속성을 생성자에 강제로 전달하지 않습니다. 대신, 종속성은 필요한 개체에 의해 노출 된 공용 속성에 설정됩니다. 앞에서 암시 한 바와 같이,이를 수행하는 주요 동기는 다음과 같습니다.

  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;

3
첫 번째 단락은 질문에서 벗어난 것으로 생각되며 DI에 대한 모든 정의가 아닙니다 (즉, DI가 아닌 SOLID를 정의하려고합니다). 기술적으로 종속성이 100 인 경우에도 종속성 주입을 계속 사용할 수 있습니다. 유사하게, 구체적인 의존성을 주입하는 것이 가능합니다. 그것은 여전히 ​​의존성 주입입니다.
Jay Sullivan

10

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

  1. 클래스에 주입 될 모든 실제 구현 (인터페이스가 아닌) (예 : 컨트롤러에 대한 서비스)에 DI 구성이있는 경우 어떤 종류의 하드 코딩이 아닌 이유는 무엇입니까?
  2. 런타임에 객체를 변경하려면 어떻게합니까? 예를 들어, MyController를 인스턴스화 할 때 구성에 이미 FileLogger를 ILogger로 주입한다고 말합니다. 그러나 DatabaseLogger를 주입하고 싶을 수도 있습니다.
  3. AClass에 필요한 객체를 변경할 때마다 클래스 자체와 구성 파일의 두 위치를 살펴 봐야합니다. 그렇게하면 인생이 더 쉬워 집니까?
  4. AClass의 Aproperty가 주입되지 않으면 그것을 조롱하기가 더 어렵습니까?
  5. 첫 번째 질문으로 돌아갑니다. new object () 사용이 나쁘다면 인터페이스가 아닌 구현을 어떻게 주입합니까? 많은 사람들이 실제로 인터페이스를 주입한다고 말하고 있지만 구성을 사용하면 런타임에 해당 인터페이스의 구현을 지정할 수 없습니다. 컴파일 시간 동안 하드 코딩됩니다.

이것은 @Adam N이 게시 한 답변을 기반으로합니다.

PersonService가 더 이상 GroupMembershipService에 대해 걱정할 필요가없는 이유는 무엇입니까? 방금 GroupMembership에 의존하는 여러 가지 (개체 / 속성)가 있다고 언급했습니다. PService에 GMService가 필요한 경우 속성으로 사용합니다. 주사 여부에 관계없이 그것을 조롱 할 수 있습니다. GMService에 더 구체적인 하위 클래스가 있는지 런타임에만 알 수 있습니다. 런타임까지 알 수 없었습니다. 그런 다음 서브 클래스를 주입하려고합니다. 또는 싱글 톤 또는 프로토 타입으로 사용하려는 경우. 솔직히 말해서, 구성 파일에는 컴파일 타임 동안 어떤 유형 (인터페이스)의 서브 클래스에 어떤 서브 클래스가 삽입 될 수 있는지에 대한 모든 것이 하드 코딩되어 있습니다.

편집하다

DI에 관한 Jose Maria Arranz의 멋진 코멘트

DI는 의존성 방향을 결정하고 글루 코드를 작성할 필요가 없어 응집력을 높입니다.

그릇된. 종속성의 방향은 XML 형식 또는 주석으로되어 있으며, 종속성은 XML 코드 및 주석으로 작성됩니다. XML과 주석은 소스 코드입니다.

DI는 모든 구성 요소를 모듈 식 (예 : 교체 가능)으로 만들고 연결이 잘 정의되어있어 커플 링을 줄입니다.

그릇된. 인터페이스를 기반으로 모듈 식 코드를 빌드하기 위해 DI 프레임 워크가 필요하지 않습니다.

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

제 생각에는 DI 프레임 워크의 유일한 이유는 보일러 플레이트 감소입니다. 잘 구성된 공장 시스템을 사용하면 선호하는 DI 프레임 워크와 동일하고보다 제어 가능하며 예측 가능한 작업을 수행 할 수 있습니다. DI 프레임 워크는 코드 축소를 보장합니다 (XML 및 주석도 소스 코드 임). 문제는 보일러 플레이트 축소가 매우 간단한 경우 (클래스 당 하나의 인스턴스와 유사한 경우)에 실제로 적용되기도하며, 실제 환경에서 적절한 서비스 개체를 선택하는 것은 클래스를 단일 개체에 매핑하는 것만 큼 쉽지 않습니다.


8

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

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

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

"종속성 주입"은 "제어 역전"의 한 유형으로 간주되는데, 이는 일부 로직이 호출자로부터 제거됨을 의미합니다. 호출자가 매개 변수를 전달하는 경우에는 해당되지 않으므로 DI 인 경우 DI는 제어 반전을 의미하지 않습니다.

DI는 호출자와 생성자 사이에 종속성을 관리하는 중간 레벨이 있음을 의미합니다. Makefile은 의존성 주입의 간단한 예입니다. "caller"는 명령 행에 "make bar"를 입력하는 사람이고 "constructor"는 컴파일러입니다. Makefile은 bar가 foo에 의존하도록 지정하고

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

하기 전에

gcc foo.o bar.o -o bar

"make bar"를 입력하는 사람은 그 바가 foo에 의존한다는 것을 알 필요가 없습니다. "make bar"와 gcc간에 종속성이 주입되었습니다.

중간 레벨의 주요 목적은 의존성을 생성자에게 전달하는 것이 아니라 모든 의존성을 곳에만 나열하는 것입니다. 하고 코더에서 숨기는 것입니다 (코더에서 제공하지 않음).

일반적으로 중간 수준은 생성 된 개체에 대한 팩토리를 제공하며 요청 된 각 개체 유형이 충족해야하는 역할을 제공해야합니다. 건설 세부 사항을 숨기는 중간 레벨을 가짐으로써 이미 공장에서 부과 한 추상화 패널티가 발생 했으므로 공장을 사용할 수도 있습니다.


8

의존성 주입 이란 코드의 한 부분 (예 : 클래스)이 하드 코딩되지 않은 모듈 식 방식으로 종속성 (다른 코드, 예를 들어 다른 클래스, 의존하는)에 액세스 할 수 있는 방법 (실제로 어쨌든 )을 의미합니다. 필요에 따라 자유롭게 변경하거나 재정의하거나 다른 시간에로드 할 수 있습니다)

(그리고 ps, 예, 다소 단순한 개념으로 지나치게 과장된 25 $ 이름이되었습니다) , 내 .25센트


8

나는 이미 많은 답변이 있지만 이것이 매우 도움이된다는 것을 알았습니다 : 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인스턴스화가 생성자로 어떻게 이동 하는지 확인하십시오 . 생성자는에 필요한 네 개의 값인 네 개의 매개 변수를 사용합니다 DataSourceImpl. MyDao클래스는 여전히이 네 가지 값에 의존 하지만 더 이상 이러한 종속성 자체를 만족시키지 않습니다. MyDao인스턴스를 생성하는 모든 클래스에서 제공됩니다 .


1
DI가 이미 생성 한 DataSourceImp를 인터페이스로 전달하지 않습니까?
PmanAce

6

의존성 주입은 일반적으로 "의존성 난독 화 (Dependency Obfuscation)"요구 사항에 대한 해결책 중 하나입니다. 의존성 난독 화는 그것을 필요로하는 클래스에 대한 의존성을 제공함으로써 어떤 식 으로든 상기 클래스에 대한 상기 의존성을 제공하는 과정에서 '명백한'성질을 취하는 방법이다. 반드시 나쁜 것은 아닙니다. 실제로, 클래스에 의존성이 제공되는 방식을 난독 화함으로써 클래스 외부의 무언가가 의존성을 생성하는 책임이 있으며, 이는 다양한 시나리오에서 변경없이 다른 의존성 구현이 클래스에 제공 될 수 있음을 의미합니다. 수업에. 이는 프로덕션 모드와 테스트 모드 간 전환에 유용합니다 (예 : '모의'서비스 종속성 사용).

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

프로그래머는 수년간 의존성 난독 화 요구 사항을 이해했으며 의존성 주입이 고안된 전후에 많은 대안 솔루션이 발전해 왔습니다. 팩토리 패턴이 있지만 특정 인스턴스에 대한 주입이 필요하지 않은 ThreadLocal을 사용하는 많은 옵션이 있습니다-종속성은 스레드에 효과적으로 주입되어 편리하게 정적 getter 메소드를 통해 객체를 사용할 수 있다는 이점이 있습니다 임의필요한 클래스에 주석을 추가하지 않고 복잡한 XML '접착제'를 설정하지 않고도 필요한 클래스. 의존성 (JPA / JDO 또는 기타)에 의존성이 필요한 경우 POJO로 구성된 도메인 모델 및 비즈니스 모델 클래스를 사용하여 훨씬 쉽게 '투명성 지속성'을 달성 할 수 있습니다.



5

기술 설명으로 이동하기 전에 먼저 의존성 주입을 배우는 많은 기술적 인 것들을 찾을 수 있지만 나와 같은 사람들이 핵심 개념을 얻을 수없는 최대 시간을 가지기 때문에 실제 예제로 먼저 시각화하십시오.

첫 번째 그림에서, 많은 단결을 가진 자동차 공장 이 있다고 가정합니다 . 자동차는 실제로 조립 장치에 내장되어 있지만 엔진 , 시트휠이 필요 합니다. 따라서 조립 장치 는 이러한 모든 장치에 의존 하며 공장 의 의존성 입니다.

주요 작업 (조립 장치에 자동차 조립)과 함께 다른 장치 에도 중점을 두어야하므로 이제이 공장에서 모든 작업을 유지 관리하기가 너무 복잡하다고 느낄 수 있습니다 . 유지 보수 비용이 많이 들고 공장 건물이 커서 임대에 추가 비용이 듭니다.

이제 두 번째 그림을보십시오. 자체 생산 비용보다 , 시트엔진을 저렴 하게 제공하는 일부 공급 업체를 찾으면 공장에서 제조 할 필요가 없습니다. 유지 보수 작업을 줄이고 추가 임대 비용을 줄일 수있는 조립 장치 용으로 더 작은 건물을 임대 할 수 있습니다 . 이제 주요 작업에만 집중할 수 있습니다 (자동차 조립).

이제 자동차 조립에 대한 모든 의존성공급 업체 로부터 공장에 주입 되었다고 말할 수 있습니다 . 실제 의존성 주입 (DI)의 예 입니다.

기술 용어에서, 의존성 주입은 한 객체 (또는 정적 메소드)가 다른 객체의 종속성을 제공하는 기술입니다. 따라서 객체를 생성하는 작업을 다른 사람에게 전달하고 종속성을 직접 사용하는 것을 종속성 주입이라고합니다.

이것은 이제 당신 기술적 인 단어로 DI를 배우는 데 도움 될 것입니다. DI를 사용할 때 표시됩니다 때이해야 하지 .

하나의 자동차 공장에서 모두.

간단한 자동차 공장


1
40 ish 중에서 가장 명확한 답변. 실제 사례 및 이미지. +1. 허용되는 답변이어야합니다.
Marche Remi

4

Book Apress.Spring.Persistence.with.Hibernate.2010 년 10 월

종속성 주입의 목적은 응용 프로그램 비즈니스 논리에서 외부 소프트웨어 구성 요소를 해결하는 작업을 분리하는 것입니다. 종속성 주입없이 구성 요소가 필요한 서비스에 액세스하는 방법에 대한 세부 정보가 구성 요소의 코드와 혼동 될 수 있습니다. 이는 오류 가능성을 높이고 코드 팽창을 추가하며 유지 관리 복잡성을 확대합니다. 구성 요소를보다 밀접하게 결합하므로 리팩토링 또는 테스트시 종속성을 수정하기가 어렵습니다.


4

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();
}
}

의존성 주입을 사용하는 방법에는 여러 가지가 있습니다. Singleton 등과 결합하는 것이 가능하지만 여전히 기본적으로 다른 객체 내에 객체 유형의 속성을 작성하여 실현됩니다. 우리가 계속해서 작성해야 할 코드는 단지 기능적인면에서만 유용하며 앞으로 우리를 위해 준비되고 완성됩니다. 이것이 DI가 IoC (Inversion of Control)와 긴밀하게 결합한 이유입니다. 즉, 프로그램이 다른 실행중인 모듈을 제어하여 코드에 Bean을 삽입하는 제어를 전달합니다. (주입 할 수있는 각 객체는 서명되거나 Bean으로 간주 될 수 있습니다.) 예를 들어 Spring에서는 생성 및 초기화를 통해 수행됩니다. ApplicationContext컨테이너, 이것은 우리를 위해 작동합니다. 우리는 단순히 코드에서 컨텍스트를 작성하고 초기화 Bean을 호출합니다. 그 순간 주입이 자동으로 완료되었습니다.


4

5 세의 의존성 주입.

냉장고에 물건을 가져 가면 문제가 발생할 수 있습니다. 당신은 문을 열어두고, 엄마 나 아빠가 원하지 않는 것을 얻을 수 있습니다. 우리가 가지고 있지 않거나 만료 된 것을 찾고있을 수도 있습니다.

당신이해야 할 일은 "나는 점심으로 마실 것이 필요합니다"라고 말하고 나서 식사를 위해 앉을 때 무언가를 갖도록 할 것입니다.


1
이것은 분명히 부모의 대답입니다. ;)
Marche Remi

4

Christoffer Noring의 Pablo Deeleman의 저서“Learning Angular-Second Edition”:

"우리의 응용 프로그램이 성장하고 발전함에 따라 각 코드 엔터티 는 내부적 으로 소프트웨어 엔지니어링 세계에서 종속성 으로 더 잘 알려진 다른 객체의 인스턴스를 필요로 합니다 . 이러한 종속성 을 종속 클라이언트 에 전달 하는 동작injection , 또한 인젝터 라는 다른 코드 엔티티의 참여도 수반합니다 . 인젝터인스턴스화부트 스트래핑에 대한 책임을집니다 . 필요한 종속성 집니다.따라서 클라이언트에 성공적으로 주입 된 순간부터 사용할 수 있습니다. 이는 클라이언트가 자신의 의존성인스턴스화 하는 방법에 대해 전혀 모르고 사용하기 위해 구현 한 인터페이스 만 알고 있기 때문에 매우 중요 합니다. "

보낸 사람 : Anton Moiseev. “Typescript를 사용한 각도 개발, 제 2 판”책 :

즉, DI느슨하게 결합 된 방식으로 코드를 작성하는 데 도움이 되며 코드를 더욱 테스트 하고 재사용 할 수있게합니다 .”


3

간단히 말하면 DI (종속성 주입)는 서로 다른 개체 간의 종속성 또는 긴밀한 연결을 제거하는 방법입니다. 의존성 주입은 각 객체에 응집력있는 동작을 제공합니다.

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

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


3

의존성 주입은 Spring Framework와 관련된 개념의 핵심입니다. 모든 프로젝트의 프레임 워크를 만드는 것이 중요한 역할을 수행 할 수 있지만 여기서 의존성 주입은 투수입니다.

실제로 java에서 클래스 A와 클래스 B로 두 개의 다른 클래스를 만들고 클래스 A에서 사용할 수있는 함수가 무엇이든 클래스 A에서 사용하려는 경우 종속성 주입을 사용할 수 있습니다. 다른 클래스에 전체 클래스를 주입하여 액세스 할 수 있도록하는 것과 같은 방식으로 한 클래스의 오브젝트를 다른 클래스에 넣을 수 있습니다. 이러한 방식으로 의존성을 극복 할 수 있습니다.

종속성 주입은 두 클래스를 간단하게 붙이고 동시에 별도의 시간을 유지합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.