design-patterns 예제 - 의존성 주입이란 무엇입니까?




단점 장점 (25)

종속성 삽입 에 대한 특정 질문과 함께 게시 할 몇 가지 질문이 있습니다. 예를 들어 언제 사용하고 프레임 워크에 어떤 질문이 있습니까? 하나,

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


Answers

이미 많은 답이 있다는 것을 알고 있습니다 만, 저는 이것이 매우 도움이된다는 것을 알았습니다 : 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인스턴스를 만드는 클래스에 의해 제공됩니다 .


"의존성 주입"은 매개 변수화 된 생성자와 공용 설정자를 사용하는 것을 의미하지 않습니까?

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 (Dependency Injection)는 OOP의 기본 기능인 하나의 객체에서 다른 객체와의 관계를 사용하는 디자인 패턴 (Design Patterns)의 객체입니다. 상속은 하나의 객체를보다 복잡하고 구체적으로 상속하는 반면 다른 객체, 관계 또는 연관은 속성을 사용하여 하나의 객체에서 다른 객체에 대한 포인터를 간단하게 만듭니다. 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가 Inversion of Control (IoC)과 밀접하게 결합 된 이유입니다. 즉, 우리 프로그램이 콩에 코드를 주입하는 다른 실행중인 모듈을 제어하는 ​​것을 의미합니다. (삽입 할 수있는 각 객체는 서명되거나 Bean으로 간주 될 수 있습니다.) 예를 들어 Spring에서는 ApplicationContext컨테이너, 이것은 우리를 위해이 일을 않습니다. 우리는 단순히 우리의 코드에서 Context를 생성하고 bean 초기화를 호출한다. 그 순간 주입이 자동으로 수행되었습니다.


의존성 주입 은 프레임 워크 기반의 " 제어 반전 (Inversion of Control) "원칙 의 구현 유형입니다 .

GoF의 "디자인 패턴"에 명시된 프레임 워크 는 개발자가이를 수행하도록하는 주요 제어 흐름 논리를 구현하는 클래스이며 프레임 워크는 제어 원리의 반전을 구현합니다.

클래스 계층 구조가 아닌 기법으로 구현하는 방법으로,이 IoC 원리는 단지 종속성 삽입입니다.

DI 는 주로 클래스 인스턴스와 해당 인스턴스에 대한 유형 참조를 외부 "엔터티"에 매핑하는 대리자로 구성됩니다. 객체, 정적 클래스, 구성 요소, 프레임 워크 등 ...

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

당연히 OOP 관점에서 원하는대로 여러 방법으로이 기술을 구현할 수 있습니다 (예 : 생성자 주입 , 설정 주입 , 인터페이스 주입 참조) .

객체에 대한 참조를 일치시키는 타사를 위임하면 동일한 서비스 구현에서 일부 서비스가 필요한 구성 요소를 완전히 분리하려는 경우 매우 유용합니다.

이러한 방식으로 컴포넌트를 설계 할 때 아키텍처와 특정 로직에만 집중할 수 있고, 사용되는 객체 / 서비스의 구현 변경 사항에 대해 걱정할 필요없이 다른 객체와 협업 할 수있는 인터페이스를 신뢰합니다. 완전히 대체 될 것입니다 (분명히 인터페이스를 존중 함).


DI (Dependency Injection)의 핵심은 애플리케이션 소스 코드를 깨끗 하고 안정적으로 유지하는 것입니다 .

  • 의존성 초기화 코드 정리
  • 의존성에 관계없이 안정 하다.

실질적으로 모든 디자인 패턴은 관심사를 분리하여 향후 변경 사항이 최소 파일에 영향을 미치도록 만듭니다.

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

예 : 쉘 스크립트가있는 DI

자바 외부에서 일하는 경우 source가 종종 있는데, 많은 스크립팅 언어 (Shell, Tcl 등, 심지어 import는 Python에서 이러한 목적으로 오용 된 것) 에서 자주 사용되는 방법을 생각해보십시오 .

간단한 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 "[email protected]"
}

source종속 스크립트에서 구현 스크립트를 직접 실행 하는 대신 injector.sh"구성 요소"를 모두 포함 하는 "컨테이너"를 사용합니다 .

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

archive_files 의존성 단지 한 주사따라 스크립트.

또는을 archive_files사용하여 구현 된 종속성을 주입 할 수 있습니다 .tarxz

예 : DI 제거

dependent.sh스크립트가 의존성을 직접 사용 했다면 , 그 접근법은 의존성 룩업 ( dependency lookup) 이라고 불리게 될 것입니다 ( 의존성 주입 과 반대입니다 ) :

#!/bin/sh
# Dependent

# dependency look-up
source ./archive_files_zip.sh

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

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

"component"소스 코드는 clean 이나 stable이 아니기 때문에 의존성 초기화의 모든 변경에는 "components"소스 코드 파일에 대한 새로운 릴리스가 필요하기 때문에 안정적이지 않습니다 .

마지막 말

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

그러나 그것은 다음과 같은 우려를 나눌 수있는 일반적인 접근 방법입니다.

  • 응용 프로그램 개발 ( 단일 소스 코드 릴리스 수명주기)
  • 응용 프로그램 배포 ( 독립 실행 형 라이프 사이클이있는 여러 대상 환경)

종속성 조회 에서만 구성을 사용하면 종속성 (예 : 새 인증 유형) 및 지원되는 종속성 유형 (예 : 새 데이터베이스 유형)의 수만큼 구성 매개 변수 수가 변경 될 수 있으므로 도움이되지 않습니다.


간단히 말해서, 의존성 삽입 (Dependency Injection, DI)은 서로 다른 객체 간의 종속성 또는 밀접한 결합을 제거하는 방법입니다. Dependency Injection은 각 객체에 응집력있는 동작을 제공합니다.

DI는 Spring의 IOC principal의 구현입니다. "우리에게 전화하지 마라."라고 적혀 있습니다. 의존성 삽입 프로그래머는 new 키워드를 사용하여 객체를 생성 할 필요가 없습니다.

객체는 일단 Spring 컨테이너에로드 된 다음 getBean (String beanName) 메소드를 사용하여 Spring 컨테이너에서 객체를 가져 와서 필요할 때마다 다시 사용합니다.


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

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

Spring에서 Dependency Injection이 어떻게 작동 하는가?

새 키워드를 사용하여 객체를 하드 코딩 할 필요가 없으며 구성 파일에서 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 기반 종속성 삽입 :

Setter 기반 DI는 빈을 인스턴스화하기 위해 인수가없는 생성자 또는 인수가없는 정적 팩토리 메소드를 호출 한 후 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"/>

참고 : 필수 의존성에 대한 생성자 인수와 선택적 종속성에 대한 설정자를 사용하는 것이 좋습니다. 우리가 setter에서 @Required annotation을 기반으로하는 annotation을 사용하면 setter를 필수 의존성으로 만드는 데 사용할 수 있습니다.


Apress.Spring.Persistence.with.Hibernate.Oct.2010에서

종속성 주입의 목적은 외부 소프트웨어 구성 요소를 응용 프로그램 비즈니스 논리에서 분리하는 작업을 분리하는 것입니다. 종속성 삽입없이 구성 요소가 필요한 서비스에 액세스하는 방법에 대한 세부 정보는 구성 요소 코드로 혼란 스러울 수 있습니다. 이렇게하면 오류 가능성이 증가하고 코드가 부풀어 오르고 유지 관리 복잡성이 커집니다. 구성 요소를 더 가깝게 연결하기 때문에 리팩토링 또는 테스트시 종속성을 수정하기가 어렵습니다.


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

(그리고 추신 : 예, 그것은 오히려 단순한 컨셉에 대해 과장된 25 $ 이름이되었습니다) , 나의 .25센트


DI (Dependency Injection) 란 무엇입니까?

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

DI, DIP 및 SOLID

특히 로버트 C 마틴 (Robert C Martin)의 객체 지향 설계 (Object Oriented Design)SOLID 원칙의 패러다임에서 DI의존성 반전 원리 (Dependency Inversion Principle, DIP) 의 가능한 구현 중 하나이다. DIP은 SOLID mantra의 D 입니다. 다른 DIP 구현에는 Service Locator 및 Plugin 패턴이 포함됩니다.

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의 한 가지 결과는 종속성 객체가 생성자 또는 설정자 주입을 통해 소비 클래스로 전달되므로 종속 객체 인스턴스의 수명 관리가 소비 클래스에 의해 더 이상 제어되지 않는다는 것입니다.

다른 방법으로 볼 수 있습니다.

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

DI를 언제 사용합니까?

  • 동등한 구현을 위해 의존성을 대체 할 필요가있을 경우,
  • 의존성을 분리하여 클래스의 메소드를 단위 테스트해야하는 경우 언제든지,
  • 의존성 수명의 불확실성이 실험을 보증 할 수있는 곳에서 (예, MyDepClass , MyDepClass 는 스레드 안전합니다 - 우리가 싱글 톤으로 만들고 모든 인스턴스에 동일한 인스턴스를 삽입한다면 어떨까요?)

다음은 간단한 C # 구현입니다. 주어진 소비 클래스 :

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

겉으로보기에는 무해하지만, System.DateTimeSystem.Console 이라는 두 가지 클래스에 static 종속성이 두 가지 있습니다.이 클래스는 로깅 출력 옵션을 제한하지 않으며 (아무도보고 있지 않으면 콘솔 로깅은 쓸모가 없습니다.) 더 나쁘고 어렵습니다. 비 결정적 시스템 클럭에 대한 종속성을 자동으로 테스트합니다.

우리는이 클래스에 DIP 을 적용 할 수 있습니다. 타임 스탬프의 관심을 종속성으로 추상화하고 MyLogger 를 간단한 인터페이스에만 연결 MyLogger 됩니다.

public interface IClock
{
    DateTime Now { get; }
}

또한 Console 에 대한 종속성을 TextWriter 와 같은 추상화로 완화 할 수 있습니다. Dependency Injection은 일반적으로 constructor 삽입 (소비 클래스의 생성자에 대한 매개 변수로 추상화를 종속으로 전달) 또는 Setter Injection ( setXyz() setter 또는 .Net Property를 통해 종속성 전달)으로 setXyz() {set;} 한정된). 생성자 삽입은 클래스가 생성 된 후에 올바른 상태가되고 내부 종속성 필드가 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 컨테이너에서 제공해야합니다)

자동화 된 유닛 테스트를 빌드 할 수 있습니다.이 테스트는 로거가 올바르게 작동하고 있음을 증명합니다. 이제는 종속성에 대한 제어권을 갖게되었습니다. 시간, 그리고 우리는 작성된 출력을 감시 할 수 있습니다.

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

다음 단계

종속성 삽입은 항상 Inversion of Control 컨테이너 (IoC) 와 연관되어 있으며, 구체적인 종속 인스턴스를 주입 (제공)하고 수명 인스턴스를 관리합니다. 구성 / 부트 스트랩 프로세스 중에 IoC 컨테이너는 다음을 정의 할 수 있습니다.

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

일반적으로 IoC 컨테이너가 구성되고 / 부트 스트랩 된 후에는 백그라운드에서 원활하게 작동하여 코더가 의존성에 대해 걱정하지 않고 직접 코드에 집중할 수있게합니다.

DI 친화적 인 코드의 핵심은 클래스의 정적 결합을 방지하고 종속성 생성을 위해 new ()를 사용하지 않는 것입니다.

위의 예에서와 같이 종속성을 분리하려면 일부 디자인 작업이 필요하며 개발자에게는 new 종속성 습관을 직접적으로 파기하고 종속성을 관리하기 위해 컨테이너를 신뢰하는 패러다임 전환이 필요합니다.

그러나 혜택은 많은 것이고 특히 관심 분야를 철저히 테스트 할 수있는 능력에 있습니다.

참고 : POCO / POJO / Serialization DTO / Entity Graphs / Anonymous JSON 투영법 (예 : "데이터 전용"클래스 또는 레코드)의 생성 / 매핑 / 투영 ( new ..() 를 통해) 메소드에서 사용되거나 반환 된 메소드는 간주 되지 않습니다. (UML의 의미에서) 종속성으로 간주되며 DI가 적용되지 않습니다. new 프로젝트를 사용하면 이것만으로도 좋습니다.


예를 들어, 우리는 2 클래스 ClientService. 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생성자 를 변경하면 모든 장소에서 코드를 변경해야합니다. create Serviceobject

의존성 삽입 사용

방법 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

장점

  • 테스트를 더 쉽게 만들기
  • the Service를 변경하면 Injector 클래스에서 변경하기 만하면됩니다.
  • use를 사용하면 Constructor Injection,의 생성자를 볼 때 클래스의 Client얼마나 많은 의존성을 볼 수 있습니다.Client

단점

  • 당신이 사용을 사용하는 경우 Constructor InjectionService객체가 될 때 생성되는 Client생성, 언젠가 우리가 기능을 사용하여 Client사용하지 않고 클래스 Service그렇게 만들어 Service낭비

의존성 주입 정의

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

의존성은 사용할 수있는 객체입니다. ( Service)
주입은 Service종속 객체 ( Client)를 의존 객체 ( )로 전달하는 것입니다


즉, 개체는 작업을 수행하는 데 필요한만큼의 종속성 만 가져야하며 종속성은 적어야합니다. 또한 객체의 종속성은 가능한 경우 "구체적인"객체가 아닌 인터페이스에 의존해야합니다. (구체적인 객체는 new라는 키워드로 생성 된 객체입니다.) 느슨한 결합은 재사용 가능성을 높이고 유지 관리가 쉬우 며 값 비싼 서비스 대신 "모의 (mock)"객체를 쉽게 제공 할 수있게합니다.

"Dependency Injection"(DI)은 "Inversion of Control"(IoC)이라고도 알려져 있으며이 느슨한 커플 링을 장려하는 기술로 사용될 수 있습니다.

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

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

생성자 주입

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

생성자는 구체적인 객체가 아니라 인터페이스를 받아들입니다. 또한 orderDao 매개 변수가 null 인 경우 예외가 발생합니다. 이는 유효한 종속성을받는 것이 중요하다는 것을 강조합니다. 생성자 삽입은 객체에 의존성을 부여하기 위해 선호되는 메커니즘입니다. 개발자가 적절한 실행을 위해 "Person"객체에 의존성을 부여해야하는 객체를 호출하는 것은 명확합니다.

세터 주입

그러나 다음 예제를 고려해보십시오. 여러분이 의존성이없는 10 개의 메소드를 가진 클래스를 가지고 있다고 가정 해 봅시다. 그러나 IDAO에 의존성을 갖는 새로운 메소드를 추가하고 있습니다. Constructor Injection을 사용하도록 생성자를 변경할 수 있지만, 이로 인해 모든 생성자 호출이 변경 될 수 있습니다. 또는, 의존성을 취하는 새로운 생성자를 추가 할 수는 있지만 개발자가 한 생성자를 다른 생성자보다 더 쉽게 사용할 수있는 방법을 쉽게 알 수있는 방법은 무엇입니까? 마지막으로 의존성을 생성하는 데 비용이 많이 드는 경우, 생성되지 않고 생성자로 전달되는 것이 왜 드문 경우입니까? "Setter Injection"은 이와 같은 상황에서 사용할 수있는 또 다른 DI 기술입니다.

Setter Injection은 종속성을 생성자에 전달하도록 강제하지 않습니다. 대신 의존성은 필요한 객체에 의해 노출 된 public 속성으로 설정됩니다. 앞서 암시했듯이이를 수행하는 주요 동기는 다음과 같습니다.

  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;

느슨한 커플 링 측면에서이 재미있는 예제를 발견했습니다.

모든 응용 프로그램은 몇 가지 유용한 작업을 수행하기 위해 서로 협업하는 많은 개체로 구성됩니다. 전통적으로 각 객체는 협업하는 종속 객체 (의존성)에 대한 자체 참조를 얻는 역할을합니다. 이로 인해 고도로 결합 된 클래스와 테스트하기 어려운 코드가 생성됩니다.

예를 들어 Car 객체를 생각해보십시오.

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

의존성 주입 (Dependency Injection, DI)없이 :

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

  //The rest
}

여기에서 Car 객체 는 종속 객체를 생성합니다.

초기 NepaliRubberWheel() 펑크 션 이후 종속 객체 (예 : Wheel 의 유형을 변경하려면 어떻게해야합니까? 우리는 ChineseRubberWheel() 과 같은 새로운 의존성을 가진 Car 객체를 재 작성해야하지만, 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;
  }
}

출처 : 의존성 주입 이해


Dependency Injection은 Spring Framework와 관련된 개념의 핵심입니다. 프로젝트 스프링의 프레임 워크를 만드는 것이 중요한 역할을 수행 할 수 있으며, 여기서 의존성 주입은 투수에 있습니다.

사실, java에서 클래스 A와 클래스 B와 같은 두 개의 다른 클래스를 만들었고, 클래스 B에서 사용할 수있는 함수가 무엇이든간에 클래스 A에서 사용하기를 원한다면, 그 당시에는 종속성 주입을 사용할 수 있습니다. 하나의 클래스의 오브젝트를 다른 클래스로 크레이트 할 수있는 것과 같은 방식으로 전체 클래스를 다른 클래스에 삽입하여 액세스 가능하게 만들 수 있습니다. 이런 식으로 의존성을 극복 할 수 있습니다.

위약 주입은 두 가지 등급으로 나누어서 동시에 동일한 시간에 두 가지 등급을 유지하는 것입니다.


의존성 주입 개념을 이해하기 쉽도록 만듭니다. 전구를 토글 (켜기 / 끄기)하는 스위치 버튼의 예를 들어 봅시다.

의존성 주입없이

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

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

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

의존성 주입

스위치는 전구가 나에게 전달되는 것을 켜거나 끌 필요가 있다는 것을 알고 있습니다. 그래서,

스위치 -> Bulb1 또는 Bulb2 또는 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(); 
    ... 
  } 
}`

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

  • 의존성 주입 없이는 모든 것을 스스로 처리해야합니다. 보트를 찾아 낚시대를 사거나 미끼를 찾아야합니다. 물론 가능하지만 많은 책임이 있습니다. 소프트웨어 용어로,이 모든 것을 검색해야한다는 의미입니다.

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


This 내가 본 Dependency InjectionDependency Injection Container 에 대한 가장 간단한 설명입니다.

의존성 주입없이

  • 응용 프로그램에는 Foo (예 : 컨트롤러)가 필요하므로 다음을 수행하십시오.
  • 응용 프로그램이 Foo를 만듭니다.
  • 응용 프로그램 호출 Foo
    • Foo가 Bar (예 : 서비스)를 필요로하므로 다음을 수행합니다.
    • 푸가 바 생성
    • Foo는 Bar에게 전화를 겁니다.
      • Bar는 Bim (서비스, 저장소, ...)을 필요로하므로 다음을 수행합니다.
      • 바가 Bim을 만듭니다.
      • 바는 뭔가를한다.

의존성 주입

  • 응용 프로그램에는 Boo가 필요한 Foo가 필요합니다. Bim이 필요합니다.
  • 응용 프로그램이 Bim을 만듭니다.
  • 응용 프로그램은 Bar를 생성하고 Bim에게줍니다.
  • 응용 프로그램은 Foo를 생성하고 바를 제공합니다.
  • 응용 프로그램 호출 Foo
    • Foo는 Bar에게 전화를 겁니다.
      • 바는 뭔가를한다.

의존성 주입 컨테이너 사용

  • 응용 프로그램에 Foo가 필요합니다.
  • 응용 프로그램은 Container에서 Foo를 가져 오므로 다음과 같이됩니다.
    • 컨테이너가 Bim을 만듭니다.
    • 컨테이너가 Bar를 생성하고 Bim에게줍니다.
    • Container가 Foo를 만들고 Bar에게 준다.
  • 응용 프로그램 호출 Foo
    • Foo는 Bar에게 전화를 겁니다.
      • 바는 뭔가를한다.

의존성 주입의존성 주입 컨테이너 는 다음과 같은 점에서 다릅니다.

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

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


DI (Dependency Injection)는 Inversion of Control (IoC)이라고도하는 Dependency Inversion Principle (DIP)의 일부입니다. 기본적으로 하나의 모 놀리 식 시스템 대신 코드를 모듈화하고 단위 테스트 할 수 있기를 원하기 때문에 DIP을 수행해야합니다. 따라서 클래스에서 분리되어 추상화 된 코드 부분을 식별하기 시작합니다. 이제 추상화 구현은 클래스 외부에서 주입해야합니다. 일반적으로 이것은 생성자를 통해 수행 할 수 있습니다. 그래서 추상화를 매개 변수로 받아들이는 생성자를 생성합니다. 이것은 생성자를 통해 종속성 주입이라고합니다. DIP, DI 및 IoC 컨테이너에 대한 자세한 설명은 Here


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

의존성 주입은 복잡한 시스템을보다 쉽게 ​​만드는 데 도움이되는 디자인 패턴 중 하나입니다.

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

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

테이프 레코더 기계의 주된 의도는 사운드를 녹음하거나 재생하는 것입니다. 시스템을 설계하는 동안 사운드 또는 음악을 녹음하거나 재생할 릴이 필요합니다. 우리는 릴을 기계 안에 넣을 수 있습니다. 또는 릴에 걸이를 제공 할 수 있습니다. 우리가 릴에 걸이를 걸고있는 두 번째 걸걸 선택하면, 우리는 음악을 바꾸는 것의 부가 혜택을 얻고 있습니다. 릴. 또한 릴의 기능을 줄여줍니다.

의존성 주입을 사용하여 얻은 주요 이점.

  • 높은 응집도 및 느슨한 결합력.
  • 의존성을 외현 화하고 책임감 만 추구하기.
  • 구성 요소로 물건 만들기 및 결합하여 높은 기능을 갖춘 대형 시스템을 형성하십시오.
  • 그것은 독립적으로 개발 되었기 때문에 고품질의 부품을 개발하는 데 도움이됩니다.
  • 하나의 구성 요소가 실패 할 경우 구성 요소를 다른 구성 요소로 대체하는 데 도움이됩니다.

이제는 이러한 개념이 프로그래밍 세계에서 잘 알려진 프레임 워크의 기초를 형성합니다. 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();
}

너는 또한 읽을 수있다.

제어 반전과 종속성 삽입의 차이점


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과 긴밀하게 결합한이 코드의 문제점과 우리가 ElectricEngine으로 변경하기로 결정하면 Car 클래스를 다시 작성해야합니다. 그리고 응용 프로그램이 커질수록 새로운 유형의 엔진을 추가하고 사용해야하는 문제와 두통이 생깁니다.

즉,이 접근법은 우리의 상위 수준의 Car 클래스가 SOLID의 Dependency Inversion Principle (DIP)을 위반하는 하위 수준의 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 인터페이스에만 의존합니다. 자, 유일한 트릭은 우리가 어떻게 자동차의 인스턴스를 생성하고 GasEngine 또는 ElectricityEngine과 같은 실제적인 구체적인 Engine 클래스를 제공 할 것인가입니다. 그것이 의존성 주입 이 들어오는 곳입니다.

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

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

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

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

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

UPDATE : Julie Lerman의 EF Core에 관한 과정을 최근에 보았고 또한 DI에 관한 그녀의 짧은 정의가 마음에 들었습니다.

Dependency Injection은 애플리케이션이 클래스를 필요로하는 클래스에 즉각적으로 삽입 할 수있게 해주는 패턴이다. 이를 통해 코드를 좀 더 느슨하게 결합 할 수 있으며 Entity Framework Core는이 동일한 서비스 시스템에 연결됩니다.


지금까지 발견 한 최고의 정의는 James Shore의 것입니다 .

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

마틴 파울러 (Martin Fowler ) 의 기사 또한 유용 할 수 있습니다.

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

의존성은 여러 가지 방법 (생성자 삽입 또는 설정자 삽입 등)을 통해 객체에 주입 할 수 있습니다. 하나의 특수 종속성 주입 프레임 워크 (예 : Spring)를 사용하여이를 수행 할 수도 있지만 반드시 필요하지는 않습니다. 이러한 프레임 워크에 종속성 주입이 필요하지 않습니다. 객체 (의존성)를 명시 적으로 인스턴스화하고 전달하는 것은 프레임 워크에 의한 주입만큼 좋은 주입입니다.


나는 모두가 DI를 위해 썼다라고 생각한다, 내가 약간의 질문을 묻게하게 해주세요..

  1. 모든 실제 구현 (인터페이스가 아닌)이 클래스에 삽입 될 DI 구성 (예 : 컨트롤러에 대한 서비스의 경우)은 왜 그런 종류의 하드 코딩이 아닌가?
  2. 런타임에 객체를 변경하려면 어떻게해야합니까? 예를 들어 My config는 이미 MyController를 인스턴스화 할 때 FileLogger를 ILogger로 주입합니다. 하지만 DatabaseLogger를 삽입하고 싶을 수도 있습니다.
  3. AClass에 필요한 객체를 변경하려고 할 때마다 클래스와 구성 파일이라는 두 가지 위치를 조사해야합니다. 그게 어떻게 삶을 편하게합니까?
  4. AClass의 Aproperty가 주입되지 않으면 그것을 조롱하는 것이 더 힘듭니다?
  5. 첫 번째 질문으로 돌아갑니다. new object ()를 사용하는 것이 나쁜 경우, 인터페이스가 아닌 구현을 어떻게 주입합니까? 나는 많은 사람들이 우리가 실제로 인터페이스를 삽입하고 있다고 말하고 있지만, 설정은 당신이 인터페이스의 구현을 명시하게한다. 런타임이 아니라 컴파일 타임 동안 하드 코드된다.

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

PersonService가 더 이상 GroupMembershipService에 대해 걱정할 필요가없는 이유는 무엇입니까? 방금 GroupMembership은 그것이 의존하는 여러 가지 (객체 / 속성)을 가지고 있다고 언급했습니다. PService에 GMService가 필요한 경우 속성으로 사용할 수 있습니다. 당신이 주사했는지 여부에 관계없이 그것을 조롱 할 수 있습니다. 내가 주입하고자하는 유일한 시간은 GMService가 런타임까지 알지 못하는보다 구체적인 하위 클래스를 가진 경우입니다. 그런 다음 하위 클래스를 주입하려고합니다. 아니면 싱글 톤이나 프로토 타입으로 사용하고 싶다면. 솔직히, 설정 파일은 컴파일 타임에 삽입 할 타입 (인터페이스)의 하위 클래스까지 모든 것을 하드 코딩했습니다.

편집하다

호세 마리아 Arranz DI에 대한 좋은 코멘트

DI는 의존성의 방향을 결정하고 접착제 코드를 작성해야하는 필요성을 없앰으로써 응집력을 높입니다.

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

DI는 모든 구성 요소를 모듈화 (즉 교체 가능)하고 서로 잘 정의 된 인터페이스를 사용하여 결합을 줄입니다.

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

replaceable : 매우 간단한 .properties 아카이브와 Class.forName을 사용하면 클래스가 변경 될 수 있음을 정의 할 수 있습니다. 어떤 종류의 코드라도 바꿀 수 있다면, 자바는 당신을위한 것이 아니며 스크립팅 언어를 사용하십시오. 그건 그렇고 : 주석은 다시 컴파일하지 않고는 변경할 수 없습니다.

필자의 의견으로는 DI 프레임 워크에 대한 유일한 이유 : 보일러 플레이트 감소. DI 시스템은 DI 프레임 워크와 마찬가지로 동일하게,보다 잘 제어되고 예측 가능하며, DI 프레임 워크는 코드 축소 (XML 및 주석도 소스 코드 임)를 약속합니다. 문제는이 보일러 플레이트 축소가 매우 단순한 경우 (클래스 당 하나의 인스턴스 및 이와 유사한 것)입니다. 때로는 현실 세계에서 적절한 서비스 객체를 선택하는 것이 클래스를 단일 객체로 매핑하는 것만 큼 쉽지는 않습니다.


Dependency Injection은 내부적으로 구성하는 대신 다른 코드 조각에서 개체의 인스턴스를받는 방식으로 개체를 디자인하는 방식입니다. 즉, 객체가 필요로하는 인터페이스를 구현하는 객체는 코드를 변경하지 않고 대체 할 수 있으므로 테스트가 간단 해지고 디커플링이 향상됩니다.

예를 들어, 다음과 같은 클립을 고려하십시오.

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::addManager PersonService::removeManager 의 구현은 GroupMembershipService 인스턴스가 필요하다. Dependency Injection이 없다면 GroupMembershipService 의 생성자에서 새로운 GroupMembershipService 를 인스턴스화하고 두 인스턴스에서 그 인스턴스 속성을 사용하는 것이 전통적인 방법 일 것이다. 그러나 GroupMembershipService 의 생성자에 여러 가지가 필요한 경우 또는 더 나쁜 경우에는 GroupMembershipService 에서 호출해야하는 초기화 "설정자"가 있고 코드가 빠르게 커지고 PersonService 이제 GroupMembershipService 뿐 아니라 GroupMembershipService 의존하는 다른 모든 것. 또한 GroupMembershipService 대한 연결은 GroupMembershipService 에 하드 코드되어 있습니다. 즉, 테스트 용도로 GroupMembershipService 를 "더미"하거나 응용 프로그램의 여러 부분에서 전략 패턴을 사용할 수 없다는 것을 의미합니다.

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


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

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

DI는 당신과 같은 Single Responsibility (SR) 원칙에 가깝게 surgeon who can concentrate on surgery만듭니다.

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

이유 : 변경 사항을 신속하게 테스트하여 시장에 적용 할 수 있도록 코드를 쉽게 테스트하고 조롱 할 수 있기를 원합니다. 게다가 당신이 더 많은 컨트롤을 가지고있는 코드베이스로 여행 할 때 당신을 도와 줄 많은 훌륭한 무료 툴 / 프레임 워크가 없을 때, 왜 그렇게하지 않았을까요?


거기에 대해 배울 때 가장 도움이 된 두 가지 사항은 다음과 같습니다.

Graham Hutton의 책 Programming in Haskell 에서 8 장 "Functional Parsers" . 이것은 실제로 모나드에 대해서는 언급하지 않고 있지만, 챕터를 통해 작업하고 실제로 모든 것을 이해할 수 있다면, 특히 일련의 바인드 작업을 평가하는 경우 모나드의 내부를 이해할 수 있습니다. 여러 번 시도해보십시오.

튜토리얼 All About Monads . 이것은 사용법에 대한 몇 가지 좋은 예를 보여 주며 Appendex의 비유가 나를 위해 일했다고 말할 수 밖에 없습니다.







design-patterns language-agnostic dependency-injection terminology