의존성 주입 에 대한 특정 질문 , 사용시기 및 프레임 워크와 같은 몇 가지 질문이 이미 게시되어 있습니다. 하나,
의존성 주입이란 무엇이며 언제 / 왜 사용해야합니까?
의존성 주입 에 대한 특정 질문 , 사용시기 및 프레임 워크와 같은 몇 가지 질문이 이미 게시되어 있습니다. 하나,
의존성 주입이란 무엇이며 언제 / 왜 사용해야합니까?
답변:
의존성 주입 은 다른 객체 나 프레임 워크 (종속성 인젝터)에 의존성을 전달하고 있습니다.
의존성 주입은 테스트를 더 쉽게 만듭니다. 주입은 constructor을 통해 수행 할 수 있습니다 .
SomeClass()
생성자는 다음과 같습니다.
public SomeClass() {
myObject = Factory.getObject();
}
문제점 : myObject
디스크 액세스 또는 네트워크 액세스와 같은 복잡한 작업 이 관련된 경우 장치 테스트를 수행 하기가 어렵 습니다 SomeClass()
. 프로그래머는 모의해야 하며 팩토리 호출을 가로 챌myObject
수 있습니다 .
대체 솔루션 :
myObject
생성자에 인수로 전달public SomeClass (MyClass myObject) {
this.myObject = myObject;
}
myObject
테스트를 쉽게 할 수 있도록 직접 전달할 수 있습니다.
의존성 주입없이 단위 테스트에서 구성 요소를 분리하는 것이 더 어렵습니다.
2013 년에이 답변을 썼을 때 Google 테스팅 블로그 의 주요 주제였습니다 . 프로그래머가 항상 런타임 디자인 (예 : 서비스 로케이터 또는 유사한 패턴)에서 추가 유연성을 필요로하는 것은 아니기 때문에 나에게 가장 큰 장점으로 남아 있습니다. 프로그래머는 종종 테스트 중에 클래스를 분리해야합니다.
지금까지 찾은 최고의 정의는 James Shore 가 정의한 것입니다 .
"의존성 주입"은 5 센트 개념의 25 달러 용어입니다. [...] 의존성 주입은 객체에 인스턴스 변수를 제공하는 것을 의미합니다. [...].
이 마틴 파울러의 글 도 유용 할 수 있습니다.
의존성 주입은 기본적으로 객체 자체를 구성하는 대신 객체가 필요로하는 객체 (종속성)를 제공합니다. 의존성을 조롱하거나 스텁 아웃 할 수 있기 때문에 테스트에 매우 유용한 기술입니다.
종속성은 생성자 주입 또는 세터 주입과 같은 많은 방법으로 객체에 주입 될 수 있습니다. 특별한 의존성 주입 프레임 워크 (예 : Spring)를 사용하여 그렇게 할 수도 있지만 반드시 필요한 것은 아닙니다. 의존성 주입을 위해 이러한 프레임 워크가 필요하지 않습니다. 명시 적으로 객체 (종속성)를 인스턴스화하고 전달하는 것은 프레임 워크에 의한 주입만큼이나 좋은 주입입니다.
느슨한 커플 링 측면 에서이 재미있는 예를 찾았습니다 .
모든 응용 프로그램은 서로 협력하여 유용한 작업을 수행하는 많은 개체로 구성됩니다. 전통적으로 각 오브젝트는 협업하는 종속 오브젝트 (종속성)에 대한 자체 참조를 가져옵니다. 이것은 고도로 결합 된 클래스와 테스트하기 어려운 코드로 이어집니다.
예를 들어, 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;
}
}
출처 : 의존성 주입 이해
new
을 타이어? 난 아니야 내가해야 할 일은 그들로부터 (매개 변수를 통해 주입) 구매하고 설치하고 wah-lah하는 것입니다! 따라서 C # 프로젝트가 기존 라이브러리 / 클래스를 사용해야한다고 프로그래밍으로 돌아 오면, 전체 프로젝트에 대한 1 개의 참조를 실행 / 디버그하는 두 가지 방법이 있습니다
new
이고 옵션 2는 매개 변수로 전달됩니다. 정확하지는 않지만 이해하기 쉬운 간단한 어리 석음.
의존성 주입은 객체를 내부적으로 구성하는 대신 다른 코드 조각으로부터 객체의 인스턴스를 수신하는 방식으로 객체를 설계하는 방법입니다. 즉, 코드를 변경하지 않고도 객체에 필요한 인터페이스를 구현하는 모든 객체를 대체 할 수있어 테스트가 간소화되고 디커플링이 향상됩니다.
예를 들어 다음과 같은 걸쇠를 고려하십시오.
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
그 일을하기 위해. 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
변화에 대해 알 필요가 없다.
받아 들일만한 대답은 좋은 것입니다.하지만 DI는 코드에서 하드 코딩 된 상수를 피하는 고전과 매우 흡사합니다.
데이터베이스 이름과 같은 상수를 사용하면 코드 내부에서 구성 파일로 빠르게 이동하고 해당 값을 포함하는 변수를 필요한 위치로 전달합니다. 그렇게하는 이유는 이러한 상수가 일반적으로 나머지 코드보다 자주 변경되기 때문입니다. 예를 들어 테스트 데이터베이스에서 코드를 테스트하려는 경우.
DI는 객체 지향 프로그래밍 세계에서 이와 유사합니다. 상수 리터럴 대신 값이 전체 객체이지만 클래스 코드에서 코드를 생성하는 이유는 비슷합니다. 객체가이를 사용하는 코드보다 자주 변경됩니다. 그러한 변경이 필요한 중요한 경우는 테스트입니다.
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는이 동일한 서비스 시스템에 플러그인됩니다.
낚시하러 가고 싶다고 상상해 봅시다.
의존성 주입이 없으면 모든 것을 스스로 관리해야합니다. 보트를 찾고, 낚싯대를 구입하고, 미끼 등을 찾는 것이 필요합니다. 물론 가능하지만, 그것은 당신에게 많은 책임이 있습니다. 소프트웨어 용어로,이 모든 것을 검색해야합니다.
의존성 주입을 사용하면 다른 사람이 모든 준비를 처리하고 필요한 장비를 사용할 수 있습니다. 보트, 낚싯대 및 미끼를 모두 사용할 수 있습니다.
이것은 내가 본 의존성 주입 및 의존성 주입 컨테이너 에 대한 가장 간단한 설명입니다 .
의존성 주입 과 의존성 주입 컨테이너 는 서로 다릅니다.
의존성 주입을 위해 컨테이너가 필요하지 않습니다. 그러나 컨테이너가 도움이 될 수 있습니다.
"종속성 주입"이 매개 변수화 된 생성자와 공용 세터를 사용하는 것을 의미하지 않습니까?
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(); ... } }
new DatabaseThingie()
유효한 myDatabase 인스턴스를 생성하지 않는 경우에만 .
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();
...
}
}`
의존성 주입 (DI)이란 무엇입니까?
다른 사람들이 말했듯이, Dependency Injection (DI) 은 우리의 관심 클래스 (소비자 클래스)가 의존하는 ( UML 의미에서 ) 다른 객체 인스턴스의 직접 생성 및 수명 관리에 대한 책임을 제거합니다 . 이러한 인스턴스는 일반적으로 생성자 매개 변수 또는 속성 설정자를 통해 소비자 클래스로 전달됩니다 (종속성 개체 인스턴스화 및 소비자 클래스로 전달은 일반적으로 IoC (Inversion of Control) 컨테이너에서 수행하지만 다른 주제입니다) .
DI, DIP 및 SOLID
특히, 로버트 C 마틴의 패러다임에 디자인을 지향 객체의 SOLID 원칙 , DI
의 가능한 구현 중 하나 인 의존 관계 역전 원칙 (DIP) . DIP는이다 D
의 SOLID
진언이 다른 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
필요에 따라 공장에서 인스턴스를 통해 인스턴스를 얻을 수 있으며 완료되면 이러한 인스턴스를 폐기 할 수 있습니다.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
전달 ) 또는 ( 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
" )IDisposable
에 Disposing
따라 종속성 과 같은 프로토콜을 인식 하고 종속성을 담당 합니다.일반적으로 IoC 컨테이너가 구성 / 부트 스트랩되면 백그라운드에서 원활하게 작동하여 코더가 종속성에 대해 걱정하지 않고 현재 코드에 집중할 수 있습니다.
DI 친화적 인 코드의 핵심은 클래스의 정적 결합을 피하고 의존성 작성에 new ()를 사용하지 않는 것입니다.
위의 예에 따라 종속성의 디커플링에는 약간의 디자인 노력이 필요하며 개발자에게는 new
종속성을 직접 습관화 하는 대신 패러다임 전환이 필요 하며 대신 컨테이너를 신뢰하여 종속성을 관리해야합니다.
그러나 장점은 특히 관심 클래스를 철저히 테스트 할 수있는 능력에 있습니다.
참고 : new ..()
POCO / POJO / Serialization DTOs / Entity Graphs / Anonymous JSON 투영 등 의 생성 / 매핑 / 투영 ( 예 : "데이터 전용"클래스 또는 레코드)-메소드에서 사용되거나 반환 된 것은 종속성으로 간주 되지 않습니다 . UML 감각) 및 DI의 영향을받지 않습니다. new
이것들을 투영하는 데 사용하면 됩니다.
DI (Dependency Injection)의 핵심은 응용 프로그램 소스 코드를 깨끗 하고 안정적으로 유지하는 것입니다 .
실제로 모든 디자인 패턴은 향후 변경 사항이 최소 파일에 영향을 미치도록 우려를 분리합니다.
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
.
경우 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 프레임 워크 에서처럼 크게 강조되고 대중화되지 않습니다.
그러나 다음과 같은 우려를 나누기위한 일반적인 접근 방식입니다.
종속성 조회 와 함께 구성 만 사용 하는 것은 지원되는 종속성 유형 (예 : 새 데이터베이스 유형)뿐만 아니라 종속성마다 구성 매개 변수 (예 : 새 인증 유형)가 변경 될 수 있으므로 도움이되지 않습니다.
위의 모든 대답이 훌륭합니다. 제 목표는 프로그래밍 지식이없는 사람도 개념을 이해할 수 있도록 간단한 방법으로 개념을 설명하는 것입니다
의존성 주입은 복잡한 시스템을보다 간단한 방식으로 만드는 데 도움이되는 디자인 패턴 중 하나입니다.
우리는 일상 생활에서이 패턴의 다양한 적용을 볼 수 있습니다. 테이프 레코더, VCD, CD 드라이브 등이 그 예입니다.
위 이미지는 20 세기 중반 Reel-to-Reel 휴대용 테이프 레코더의 이미지입니다. 소스 .
테이프 레코더 기계의 주요 목적은 사운드를 녹음하거나 재생하는 것입니다.
시스템을 설계하는 동안 사운드 나 음악을 녹음하거나 재생하려면 릴이 필요합니다. 이 시스템을 설계 할 수있는 두 가지 가능성이 있습니다
첫 번째를 사용하는 경우 릴을 변경하려면 기계를 열어야합니다. 우리가 두 번째 것을 선택한다면, 그것은 릴을위한 고리를 놓는 것입니다. 우리는 릴을 바꿔서 음악을 연주 할 수있는 추가적인 이점을 얻고 있습니다. 또한 릴에있는 것만 재생하는 기능을 줄입니다.
현명한 의존성 주입은 의존성을 외부화하여 구성 요소의 특정 기능에만 중점을 두어 독립적 인 구성 요소를 결합하여 복잡한 시스템을 형성 할 수있는 프로세스입니다.
의존성 주입을 사용하여 달성 한 주요 이점.
오늘날 이러한 개념은 프로그래밍 세계에서 잘 알려진 프레임 워크의 기초를 형성합니다. 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();
}
당신은 또한 읽을 수 있습니다
의존성 주입 (DI)은 서로 의존하는 객체를 분리하는 것을 의미합니다. 객체 A가 객체 B에 종속되어 있다고 생각하면 이러한 객체를 서로 분리하는 것이 좋습니다. 컴파일 타임에도 불구하고 런타임에 객체에 종속성을 공유하는 대신 새로운 키워드를 사용하여 객체를 하드 코딩 할 필요가 없습니다. 우리가 이야기하면
new 키워드를 사용하여 오브젝트를 하드 코딩 할 필요는 없으며 구성 파일에서 Bean 종속성을 정의하십시오. 스프링 컨테이너는 모든 연결을 담당합니다.
IOC는 일반적인 개념이며 다양한 방식으로 표현 될 수 있으며 Dependency Injection은 IOC의 구체적인 예입니다.
생성자 기반 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>
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 어노테이션에 기반한 어노테이션을 사용하는 경우 세터를 필수 종속성으로 만드는 데 사용할 수 있습니다.
내가 생각할 수있는 가장 좋은 비유는 수술실에서 외과의와 그의 조수입니다. 외과의는 주된 사람이자 외과의가 그에게 집중할 수 있도록 필요할 때 다양한 외과 용 구성 요소를 제공하는 조수입니다. 그가 최선을 다하는 것 (외과). 보조자가 없으면 의사는 필요할 때마다 구성 요소를 직접 가져와야합니다.
간단히 말해 DI는 종속 구성 요소를 제공하여 구성 요소에 대한 일반적인 추가 책임 (부담)을 제거하는 기술입니다.
DI는 다음과 같은 SR (Single Responsibility) 원칙에 더 가깝습니다 surgeon who can concentrate on surgery
.
DI 사용시기 : 거의 모든 프로덕션 프로젝트 (소규모 / 대규모), 특히 끊임없이 변화하는 비즈니스 환경에서 DI를 사용하는 것이 좋습니다. :)
이유 : 코드를 쉽게 테스트하고 조롱 할 수 있기 때문에 변경 사항을 신속하게 테스트하여 시장에 출시 할 수 있습니다. 게다가 더 많은 제어가 가능한 코드베이스로의 여행을 지원하는 멋진 무료 도구 / 프레임 워크가 많을 때 왜 그렇지 않겠습니까?
예를 들어 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
, 당신은 단지 인젝터 클래스를 변경해야Constructor Injection
하면의 생성자를 보면 클래스의 Client
종속성이 몇 개인 지 알 수 있습니다.Client
단점
Constructor Injection
하면 Service
객체가 생성 될 때 Client
생성되며 때로는 사용 Client
하지 않고 클래스 에서 함수를 사용 Service
하므로 생성 된 Service
낭비https://en.wikipedia.org/wiki/Dependency_injection
종속성 (사용할 수있는 객체이다
Service
)
사출 종속성의 통과 (인Service
종속 객체) (Client
)을 사용할 것이라고
즉, 개체는 작업을 수행하는 데 필요한만큼의 종속성 만 가져야하며 종속성은 적어야합니다. 또한 객체의 종속성은 가능한 경우 "콘크리트"객체가 아닌 인터페이스에 있어야합니다. (콘크리트 객체는 new 키워드로 생성 된 객체입니다.) 느슨한 커플 링은 재사용 성이 향상되고 유지 관리가 용이하며 고가의 서비스 대신 "모의"객체를 쉽게 제공 할 수 있습니다.
"종속 주입"(DI)은 "제어 역전"(IoC)으로도 알려져 있으며,이 느슨한 결합을 장려하는 기술로 사용될 수 있습니다.
DI를 구현하는 데는 두 가지 주요 접근 방식이 있습니다.
객체 의존성을 생성자에 전달하는 기술입니다.
생성자는 구체적인 객체가 아닌 인터페이스를 허용합니다. 또한 orderDao 매개 변수가 널인 경우 예외가 발생합니다. 이것은 유효한 의존성을받는 것의 중요성을 강조합니다. 제 생각에는 생성자 주입은 객체에 종속성을 부여하는 데 선호되는 메커니즘입니다. 적절한 실행을 위해“Person”객체에 종속되어야하는 객체를 호출하는 동안 개발자에게 분명합니다.
그러나 다음 예제를 고려해보십시오. 종속성이없는 10 개의 메소드가있는 클래스가 있지만 IDAO에 종속 된 새 메소드를 추가한다고 가정하십시오. 생성자 생성자를 사용하도록 생성자를 변경할 수 있지만 이로 인해 모든 곳에서 모든 생성자 호출을 변경해야 할 수 있습니다. 또는 의존성을 취하는 새로운 생성자를 추가 할 수 있지만 개발자는 한 생성자를 다른 생성자보다 언제 사용 해야하는지 쉽게 알 수 있습니다. 마지막으로, 종속성이 작성하는 데 비용이 많이 드는 경우, 드물게 사용될 수있을 때 작성하여 작성해야하는 이유는 무엇입니까? "세터 주입"은 이와 같은 상황에서 사용할 수있는 또 다른 DI 기술입니다.
Setter Injection은 종속성을 생성자에 강제로 전달하지 않습니다. 대신, 종속성은 필요한 개체에 의해 노출 된 공용 속성에 설정됩니다. 앞에서 암시 한 바와 같이,이를 수행하는 주요 동기는 다음과 같습니다.
위 코드가 어떻게 보이는지에 대한 예는 다음과 같습니다.
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;
모두가 DI를 위해 글을 썼기 때문에 몇 가지 질문을하겠습니다 ..
이것은 @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 및 주석도 소스 코드 임). 문제는 보일러 플레이트 축소가 매우 간단한 경우 (클래스 당 하나의 인스턴스와 유사한 경우)에 실제로 적용되기도하며, 실제 환경에서 적절한 서비스 개체를 선택하는 것은 클래스를 단일 개체에 매핑하는 것만 큼 쉽지 않습니다.
인기있는 답변은 유용하지 않은 방식으로 의존성 주입을 정의하기 때문에 도움이되지 않습니다. "종속성 (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간에 종속성이 주입되었습니다.
중간 레벨의 주요 목적은 의존성을 생성자에게 전달하는 것이 아니라 모든 의존성을 한 곳에만 나열하는 것입니다. 하고 코더에서 숨기는 것입니다 (코더에서 제공하지 않음).
일반적으로 중간 수준은 생성 된 개체에 대한 팩토리를 제공하며 요청 된 각 개체 유형이 충족해야하는 역할을 제공해야합니다. 건설 세부 사항을 숨기는 중간 레벨을 가짐으로써 이미 공장에서 부과 한 추상화 패널티가 발생 했으므로 공장을 사용할 수도 있습니다.
나는 이미 많은 답변이 있지만 이것이 매우 도움이된다는 것을 알았습니다 : 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
인스턴스를 생성하는 모든 클래스에서 제공됩니다 .
의존성 주입은 일반적으로 "의존성 난독 화 (Dependency Obfuscation)"요구 사항에 대한 해결책 중 하나입니다. 의존성 난독 화는 그것을 필요로하는 클래스에 대한 의존성을 제공함으로써 어떤 식 으로든 상기 클래스에 대한 상기 의존성을 제공하는 과정에서 '명백한'성질을 취하는 방법이다. 반드시 나쁜 것은 아닙니다. 실제로, 클래스에 의존성이 제공되는 방식을 난독 화함으로써 클래스 외부의 무언가가 의존성을 생성하는 책임이 있으며, 이는 다양한 시나리오에서 변경없이 다른 의존성 구현이 클래스에 제공 될 수 있음을 의미합니다. 수업에. 이는 프로덕션 모드와 테스트 모드 간 전환에 유용합니다 (예 : '모의'서비스 종속성 사용).
불행히도 나쁜 부분은 일부 사람들이 의존성 난독 화를 수행하기 위해 특수 프레임 워크가 필요하다고 가정하고 특정 프레임 워크를 사용하지 않기로 결정한 경우 어떻게 든 더 적은 프로그래머입니다. 많은 사람들이 믿는 또 다른 극도로 혼란스러운 신념은 의존성 주입이 의존성 난독 화를 달성하는 유일한 방법이라는 것입니다. 이것은 명백하고 역사적으로 명백히 100 % 잘못되었지만 일부 사람들에게 의존성 난독 화 요구 사항에 대한 의존성 주입에 대한 대안이 있다는 것을 확신시키는 데 어려움을 겪을 것입니다.
프로그래머는 수년간 의존성 난독 화 요구 사항을 이해했으며 의존성 주입이 고안된 전후에 많은 대안 솔루션이 발전해 왔습니다. 팩토리 패턴이 있지만 특정 인스턴스에 대한 주입이 필요하지 않은 ThreadLocal을 사용하는 많은 옵션이 있습니다-종속성은 스레드에 효과적으로 주입되어 편리하게 정적 getter 메소드를 통해 객체를 사용할 수 있다는 이점이 있습니다 임의필요한 클래스에 주석을 추가하지 않고 복잡한 XML '접착제'를 설정하지 않고도 필요한 클래스. 의존성 (JPA / JDO 또는 기타)에 의존성이 필요한 경우 POJO로 구성된 도메인 모델 및 비즈니스 모델 클래스를 사용하여 훨씬 쉽게 '투명성 지속성'을 달성 할 수 있습니다.
책에서 ' 잘 다듬어 진 Java 개발자 : Java 7 및 폴리 글 로트 프로그래밍의 핵심 기술
DI는 특정 형태의 IoC이며, 따라서 종속성을 찾는 프로세스는 현재 실행중인 코드를 직접 제어 할 수 없습니다.
기술 설명으로 이동하기 전에 먼저 의존성 주입을 배우는 많은 기술적 인 것들을 찾을 수 있지만 나와 같은 사람들이 핵심 개념을 얻을 수없는 최대 시간을 가지기 때문에 실제 예제로 먼저 시각화하십시오.
첫 번째 그림에서, 많은 단결을 가진 자동차 공장 이 있다고 가정합니다 . 자동차는 실제로 조립 장치에 내장되어 있지만 엔진 , 시트 및 휠이 필요 합니다. 따라서 조립 장치 는 이러한 모든 장치에 의존 하며 공장 의 의존성 입니다.
주요 작업 (조립 장치에 자동차 조립)과 함께 다른 장치 에도 중점을 두어야하므로 이제이 공장에서 모든 작업을 유지 관리하기가 너무 복잡하다고 느낄 수 있습니다 . 유지 보수 비용이 많이 들고 공장 건물이 커서 임대에 추가 비용이 듭니다.
이제 두 번째 그림을보십시오. 자체 생산 비용보다 휠 , 시트 및 엔진을 저렴 하게 제공하는 일부 공급 업체를 찾으면 공장에서 제조 할 필요가 없습니다. 유지 보수 작업을 줄이고 추가 임대 비용을 줄일 수있는 조립 장치 용으로 더 작은 건물을 임대 할 수 있습니다 . 이제 주요 작업에만 집중할 수 있습니다 (자동차 조립).
이제 자동차 조립에 대한 모든 의존성 이 공급 업체 로부터 공장에 주입 되었다고 말할 수 있습니다 . 실제 의존성 주입 (DI)의 예 입니다.
기술 용어에서, 의존성 주입은 한 객체 (또는 정적 메소드)가 다른 객체의 종속성을 제공하는 기술입니다. 따라서 객체를 생성하는 작업을 다른 사람에게 전달하고 종속성을 직접 사용하는 것을 종속성 주입이라고합니다.
이것은 이제 당신 이 기술적 인 단어로 DI를 배우는 데 도움 이 될 것입니다. 이 DI를 사용할 때 표시됩니다 때이해야 하지 .
Book Apress.Spring.Persistence.with.Hibernate.2010 년 10 월
종속성 주입의 목적은 응용 프로그램 비즈니스 논리에서 외부 소프트웨어 구성 요소를 해결하는 작업을 분리하는 것입니다. 종속성 주입없이 구성 요소가 필요한 서비스에 액세스하는 방법에 대한 세부 정보가 구성 요소의 코드와 혼동 될 수 있습니다. 이는 오류 가능성을 높이고 코드 팽창을 추가하며 유지 관리 복잡성을 확대합니다. 구성 요소를보다 밀접하게 결합하므로 리팩토링 또는 테스트시 종속성을 수정하기가 어렵습니다.
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을 호출합니다. 그 순간 주입이 자동으로 완료되었습니다.
5 세의 의존성 주입.
냉장고에 물건을 가져 가면 문제가 발생할 수 있습니다. 당신은 문을 열어두고, 엄마 나 아빠가 원하지 않는 것을 얻을 수 있습니다. 우리가 가지고 있지 않거나 만료 된 것을 찾고있을 수도 있습니다.
당신이해야 할 일은 "나는 점심으로 마실 것이 필요합니다"라고 말하고 나서 식사를 위해 앉을 때 무언가를 갖도록 할 것입니다.
Christoffer Noring의 Pablo Deeleman의 저서“Learning Angular-Second Edition”:
"우리의 응용 프로그램이 성장하고 발전함에 따라 각 코드 엔터티 는 내부적 으로 소프트웨어 엔지니어링 세계에서 종속성 으로 더 잘 알려진 다른 객체의 인스턴스를 필요로 합니다 . 이러한 종속성 을 종속 클라이언트 에 전달 하는 동작 은 injection , 또한 인젝터 라는 다른 코드 엔티티의 참여도 수반합니다 . 인젝터 는 인스턴스화 및 부트 스트래핑에 대한 책임을집니다 . 필요한 종속성 집니다.따라서 클라이언트에 성공적으로 주입 된 순간부터 사용할 수 있습니다. 이는 클라이언트가 자신의 의존성 을 인스턴스화 하는 방법에 대해 전혀 모르고 사용하기 위해 구현 한 인터페이스 만 알고 있기 때문에 매우 중요 합니다. "
보낸 사람 : Anton Moiseev. “Typescript를 사용한 각도 개발, 제 2 판”책 :
즉, DI 는 느슨하게 결합 된 방식으로 코드를 작성하는 데 도움이 되며 코드를 더욱 테스트 하고 재사용 할 수있게합니다 .”
간단히 말하면 DI (종속성 주입)는 서로 다른 개체 간의 종속성 또는 긴밀한 연결을 제거하는 방법입니다. 의존성 주입은 각 객체에 응집력있는 동작을 제공합니다.
DI는 Spring의 IOC 교장의 구현으로 "우리에게 전화하지 마십시오"라고 말합니다. 의존성 주입 프로그래머를 사용하면 새로운 키워드를 사용하여 객체를 만들 필요가 없습니다.
객체는 한 번 스프링 컨테이너에로드 된 다음 getBean (String beanName) 메소드를 사용하여 스프링 컨테이너에서 해당 객체를 가져 와서 필요할 때마다 재사용합니다.
의존성 주입은 Spring Framework와 관련된 개념의 핵심입니다. 모든 프로젝트의 프레임 워크를 만드는 것이 중요한 역할을 수행 할 수 있지만 여기서 의존성 주입은 투수입니다.
실제로 java에서 클래스 A와 클래스 B로 두 개의 다른 클래스를 만들고 클래스 A에서 사용할 수있는 함수가 무엇이든 클래스 A에서 사용하려는 경우 종속성 주입을 사용할 수 있습니다. 다른 클래스에 전체 클래스를 주입하여 액세스 할 수 있도록하는 것과 같은 방식으로 한 클래스의 오브젝트를 다른 클래스에 넣을 수 있습니다. 이러한 방식으로 의존성을 극복 할 수 있습니다.
종속성 주입은 두 클래스를 간단하게 붙이고 동시에 별도의 시간을 유지합니다.