의존성 주입 에 대한 특정 질문과 함께 이미 게시된 몇 가지 질문이 있습니다. 예를 들어 언제 사용하고 어떤 프레임워크가 있는지 등입니다. 하지만,
의존성 주입이란 무엇이며 언제/왜 사용해야 합니까?
질문자 :AR.
의존성 주입 에 대한 특정 질문과 함께 이미 게시된 몇 가지 질문이 있습니다. 예를 들어 언제 사용하고 어떤 프레임워크가 있는지 등입니다. 하지만,
의존성 주입이란 무엇이며 언제/왜 사용해야 합니까?
지금까지 내가 찾은 최고의 정의는 James Shore의 정의입니다 .
"Dependency Injection"은 5센트 개념에 대한 25달러 용어입니다. [...] 종속성 주입은 개체에 인스턴스 변수를 제공하는 것을 의미합니다. [...].
Martin Fowler의 기사 도 유용할 수 있습니다.
종속성 주입은 기본적으로 개체가 자체적으로 구성하도록 하는 대신 개체에 필요한 개체(그 종속성)를 제공하는 것입니다. 종속성을 조롱하거나 스텁 아웃할 수 있으므로 테스트에 매우 유용한 기술입니다.
종속성은 다양한 방법(예: 생성자 주입 또는 설정자 주입)을 통해 개체에 주입할 수 있습니다. 특별한 의존성 주입 프레임워크(예: Spring)를 사용하여 그렇게 할 수도 있지만 반드시 필요한 것은 아닙니다. 종속성 주입을 위해 이러한 프레임워크가 필요하지 않습니다. 객체(종속성)를 명시적으로 인스턴스화하고 전달하는 것은 프레임워크에 의한 주입만큼 좋은 주입입니다.
종속성 주입 은 다른 개체 또는 프레임워크 ( 종속성 주입기)에 종속성을 전달하는 것입니다.
의존성 주입은 테스트를 더 쉽게 만듭니다. 생성자를 통해 주입을 수행할 수 있습니다.
SomeClass()
에는 다음과 같은 생성자가 있습니다.
public SomeClass() { myObject = Factory.getObject(); }
문제 : myObject
가 디스크 액세스 또는 네트워크 액세스와 같은 복잡한 작업을 포함하는 SomeClass()
에 대한 단위 테스트를 수행하기 어렵 습니다. myObject
를 조롱해야 하며 팩토리 호출을 가로챌 수 있습니다.
대체 솔루션 :
myObject
전달 public SomeClass (MyClass myObject) { this.myObject = myObject; }
myObject
를 직접 전달할 수 있으므로 테스트가 더 쉽습니다.
의존성 주입 없이 단위 테스트에서 구성 요소를 분리하는 것이 더 어렵습니다.
2013년에 이 답변을 작성했을 때 이것은 Google 테스팅 블로그 의 주요 주제였습니다. 프로그래머가 런타임 디자인(예: 서비스 로케이터 또는 유사한 패턴)에서 항상 추가 유연성을 필요로 하는 것은 아니기 때문에 이것이 나에게 가장 큰 이점으로 남아 있습니다. 프로그래머는 테스트 중에 클래스를 분리해야 하는 경우가 많습니다.
느슨한 결합 측면에서 다음과 같은 재미있는 예를 찾았습니다.
출처: 의존성 주입 이해
모든 응용 프로그램은 유용한 작업을 수행하기 위해 서로 협력하는 많은 개체로 구성됩니다. 전통적으로 각 개체는 자신과 협력하는 종속 개체(종속성)에 대한 자체 참조를 얻을 책임이 있습니다. 이것은 고도로 결합된 클래스와 테스트하기 어려운 코드로 이어집니다.
예를 들어, Car
객체를 고려하십시오.
Car
는 바퀴, 엔진, 연료, 배터리 등에 달려 있습니다. Car
객체의 정의와 함께 이러한 종속 객체의 브랜드를 정의합니다.
종속성 주입(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
에 주입될 수 있습니다.
의존성 주입을 사용한 후:
여기에서는 런타임에 종속성 (휠 및 배터리)을 주입 합니다. 따라서 용어 : 종속성 주입. 우리는 일반적으로 종속성을 생성하고 필요한 곳에 주입하기 위해 Spring, Guice, Weld와 같은 DI 프레임워크에 의존합니다.
class Car{ private Wheel wh; // Inject an Instance of Wheel (dependency of car) at runtime private Battery bt; // Inject an Instance of Battery (dependency of car) at runtime Car(Wheel wh,Battery bt) { this.wh = wh; this.bt = bt; } //Or we can have setters void setWheel(Wheel wh) { this.wh = wh; } }
장점은 다음과 같습니다.
종속성 주입은 내부적으로 구성하는 대신 다른 코드 조각에서 개체의 인스턴스를 받는 방식으로 개체를 설계하는 방법입니다. 이는 객체에 필요한 인터페이스를 구현하는 모든 객체가 코드를 변경하지 않고 대체될 수 있음을 의미하므로 테스트가 간소화되고 분리가 향상됩니다.
예를 들어 다음 클래스를 고려하십시오.
public class PersonService { public void addManager( Person employee, Person newManager ) { ... } public void removeManager( Person employee, Person oldManager ) { ... } public Group getGroupByManager( Person manager ) { ... } } public class GroupMembershipService() { public void addPersonToGroup( Person person, Group group ) { ... } public void removePersonFromGroup( Person person, Group group ) { ... } }
이 예에서 PersonService::addManager
및 PersonService::removeManager
의 구현은 작업을 수행하기 위해 GroupMembershipService
의 인스턴스가 필요합니다. 종속성 주입이 없으면 이를 수행하는 전통적인 방법 PersonService
의 생성자에서 GroupMembershipService
를 인스턴스화하고 두 함수에서 해당 인스턴스 속성을 사용하는 것입니다. GroupMembershipService
의 생성자에 GroupMembershipService
에서 호출해야 하는 일부 초기화 "설정자"가 있는 경우 코드가 다소 빠르게 증가하고 PersonService
이제 GroupMembershipService
뿐만 아니라 GroupMembershipService
의존하는 다른 모든 것. 또한,에 연결 GroupMembershipService
에 하드 코딩되어 PersonService
하는 당신이 "최대 더미"할 수 없음을 의미 GroupMembershipService
테스트 할 목적으로, 또는 응용 프로그램의 다른 부분에 전략 패턴을 사용 할 수 있습니다.
의존성 삽입 (Dependency Injection)으로, 대신 인스턴스의 GroupMembershipService
하여 내 PersonService
, 당신이 중 하나에 전달할 것 PersonService
생성자, 그렇지 않으면 로컬 인스턴스를 설정하는 속성 (getter 및 setter)를 추가합니다. PersonService
GroupMembershipService
를 생성하는 방법에 대해 걱정할 필요가 없다는 것을 의미합니다. 이것은 또한의 서브 클래스입니다 아무 의미 GroupMembershipService
1, 또는 구현 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 클래스를 다시 작성해야 합니다. 그리고 애플리케이션이 클수록 새로운 유형의 엔진을 추가하고 사용해야 하는 문제와 골칫거리가 늘어납니다.
즉, 이 접근 방식은 높은 수준의 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과 같은 실제 구체적인 Engine 클래스를 제공하는 방법입니다. 그것이 의존성 주입 이 들어오는 곳입니다.
Car gasCar = new Car(new GasEngine()); gasCar.Run(); Car electroCar = new Car(new ElectricityEngine()); electroCar.Run();
여기서는 기본적으로 종속성(Engine 인스턴스)을 Car 생성자에 주입(전달)합니다. 이제 우리 클래스는 객체와 그 종속성 사이에 느슨한 결합을 가지며 Car 클래스를 변경하지 않고도 새로운 유형의 엔진을 쉽게 추가할 수 있습니다.
종속성 주입 의 주요 이점은 클래스에 하드 코딩된 종속성이 없기 때문에 더 느슨하게 결합된다는 것입니다. 이는 위에서 언급한 종속성 반전 원칙을 따릅니다. 특정 구현을 참조하는 대신 클래스는 클래스가 생성될 때 제공되는 추상화(일반적으로 인터페이스)를 요청합니다.
따라서 결국 종속성 주입 은 개체와 해당 종속성 간의 느슨한 결합을 달성하기 위한 기술일 뿐입니다. 클래스가 작업을 수행하기 위해 필요로 하는 종속성을 직접 인스턴스화하는 대신 생성자 주입을 통해 종속성이 클래스에 제공됩니다(대부분).
또한 종속성이 많을 때 IoC(Inversion of Control) 컨테이너를 사용하는 것이 매우 좋은 방법입니다. 이 컨테이너는 어떤 인터페이스가 모든 종속성에 대해 어떤 구체적인 구현에 매핑되어야 하는지 알 수 있고 구성할 때 이러한 종속성을 해결하도록 할 수 있습니다. 우리의 개체. 예를 들어 IoC 컨테이너에 대한 매핑에서 IEngine 종속성을 GasEngine 클래스에 매핑하도록 지정할 수 있으며 IoC 컨테이너에 Car 클래스의 인스턴스를 요청할 때 GasEngine 종속성이 있는 Car 클래스를 자동으로 생성합니다. 합격했다.
업데이트: 최근 Julie Lerman의 EF Core에 대한 과정을 시청했으며 DI에 대한 짧은 정의도 좋아했습니다.
종속성 주입은 응용 프로그램이 해당 클래스가 해당 개체를 담당하도록 하지 않고 필요한 클래스에 즉시 개체를 주입할 수 있도록 하는 패턴입니다. 이를 통해 코드를 보다 느슨하게 결합할 수 있으며 Entity Framework Core가 이와 동일한 서비스 시스템에 연결됩니다.
낚시를 하고 싶다고 가정해 봅시다.
의존성 주입이 없으면 모든 것을 스스로 처리해야 합니다. 배를 구하고, 낚싯대를 사거나, 미끼를 구하는 등의 일을 해야 합니다. 물론 가능합니다. 하지만 그만큼 책임이 따릅니다. 소프트웨어 측면에서 이는 이러한 모든 항목에 대해 조회를 수행해야 함을 의미합니다.
의존성 주입을 사용하면 다른 사람이 모든 준비를 처리하고 필요한 장비를 사용할 수 있도록 합니다. 보트, 낚싯대 및 미끼를 모두 사용할 준비가 된("주사") 받게 됩니다.
이것은 내가 본 Dependency Injection 및 Dependency Injection Container 에 대한 가장 간단한 설명입니다.
의존성 주입 과 의존성 주입 컨테이너 는 다릅니다:
의존성 주입을 수행하기 위해 컨테이너가 필요하지 않습니다. 그러나 컨테이너가 도움이 될 수 있습니다.
"종속성 주입"은 매개변수화된 생성자와 공개 설정자를 사용하는 것을 의미하지 않습니까?
James Shore의 기사는 비교를 위해 다음 예를 보여줍니다 .
의존성 주입이 없는 생성자:
public class Example { private DatabaseThingie myDatabase; public Example() { myDatabase = new DatabaseThingie(); } public void doStuff() { ... myDatabase.getData(); ... } }
의존성 주입이 있는 생성자:
public class Example { private DatabaseThingie myDatabase; public Example(DatabaseThingie useThisDatabaseInstead) { myDatabase = useThisDatabaseInstead; } public void doStuff() { ... myDatabase.getData(); ... } }
기술 설명으로 이동하기 전에 먼저 실제 예제로 시각화하십시오. 종속성 주입을 배우기 위해 많은 기술적인 내용을 찾을 수 있지만 대다수의 사람들이 핵심 개념을 이해하지 못하기 때문입니다.
첫 번째 그림에서 많은 장치 가 있는 자동차 공장이 있다고 가정합니다. 자동차는 실제로 조립 장치로 만들어 지지만 엔진 , 좌석 및 바퀴가 필요 합니다. 따라서 조립 단위 는 이러한 모든 단위에 종속 되며 공장의 종속성입니다.
이제 이 공장에서 모든 작업을 유지하기가 너무 복잡하다는 것을 느낄 수 있습니다. 왜냐하면 주요 작업(조립 장치에서 자동차 조립)과 함께 다른 장치 에도 집중해야 하기 때문입니다. 지금은 유지 관리 비용이 많이 들고 공장 건물이 거대하기 때문에 임대료에 추가 비용이 필요합니다.
자, 두 번째 사진을 보시죠. 자체 생산 비용보다 저렴하게 휠 , 시트 및 엔진 을 제공하는 공급업체를 찾으면 이제 공장에서 만들 필요가 없습니다. 유지 보수 작업을 줄이고 추가 임대 비용을 줄일 수 있는 조립 장치 용으로 더 작은 건물을 지금 임대할 수 있습니다. 이제 주요 작업(자동차 조립)에만 집중할 수도 있습니다.
이제 우리는 자동차 조립을 위한 모든 종속성 이 공급자 로부터 공장에 주입 되었다고 말할 수 있습니다. 실제 DI(종속성 주입)의 예 입니다.
이제 기술 용어에서 종속성 주입은 한 개체(또는 정적 메서드)가 다른 개체의 종속성을 제공하는 기술입니다. 따라서 객체를 생성하는 작업을 다른 사람에게 양도하고 의존성을 직접 사용하는 것을 의존성 주입이라고 합니다.
이제 기술적인 설명과 함께 DI를 배우는 데 도움 이 될 것입니다. 이것은 DI를 사용해야 할 때와 사용 하지 말아야 할 때를 보여줍니다.
의존성 주입 개념을 이해하기 쉽게 만들기 위해. 전구를 토글(켜기/끄기)하는 스위치 버튼의 예를 들어보겠습니다.
스위치는 내가 연결된 전구를 미리 알아야 합니다(하드 코딩된 종속성). 그래서,
Switch -> 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(); ... } }`
의존성 주입(DI)이란?
다른 사람들이 말했듯이 의존성 주입(DI) 은 우리의 관심 클래스(소비자 클래스)가 의존하는 다른 객체 인스턴스의 직접 생성 및 수명 관리의 책임을 제거합니다( UML 의미에서 ). 이러한 인스턴스 대신 일반적으로 생성자 매개 변수로 또는 재산 세터를 통해, 우리의 소비자 클래스로 전달 (인스턴스화 및 소비자 클래스에 통과하는 종속성 개체의 관리 일반적으로 제어의 반전 (IOC의) 컨테이너에 의해 수행되지만, 다른 주제의이)된다 .
DI, DIP 및 SOLID
특히, 객체 지향 설계 의 Robert C Martin의 SOLID 원칙 패러다임에서 DI
는 DIP(Dependency Inversion Principle) 의 가능한 구현 중 하나입니다. DIP는 SOLID
만트라 D
입니다. 다른 DIP 구현에는 서비스 로케이터 및 플러그인 패턴이 포함됩니다.
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의 결과 중 하나는 종속성 개체가 이제 생성자 또는 설정자 주입을 통해 소비 클래스로 전달되기 때문에 종속성 개체 인스턴스의 수명 관리가 더 이상 소비 클래스에 의해 제어되지 않는다는 것입니다.
이것은 다양한 방식으로 볼 수 있습니다:
Create
을 통해 인스턴스를 얻을 수 있으며 완료되면 이러한 인스턴스를 삭제할 수 있습니다.DI는 언제 사용합니까?
MyDepClass
는 스레드로부터 안전합니다. 단일 항목으로 만들고 모든 소비자에게 동일한 인스턴스를 주입하면 어떻게 될까요?)예시
다음은 간단한 C# 구현입니다. 아래의 소비 클래스가 주어지면 :
public class MyLogger { public void LogRecord(string somethingToLog) { Console.WriteLine("{0:HH:mm:ss} - {1}", DateTime.Now, somethingToLog); } }
겉보기에는 무해한 것처럼 보이지만 System.DateTime
및 System.Console
static
종속성이 있습니다. 이 종속성은 로깅 출력 옵션을 제한할 뿐만 아니라(아무도 보고 있지 않은 경우 콘솔에 로깅하는 것은 무의미함) 더 나쁘게는 어렵습니다. 비결정적 시스템 클록에 대한 종속성이 주어지면 자동으로 테스트합니다.
그러나 타임스탬프에 대한 관심을 종속성으로 추상화하고 MyLogger
를 간단한 인터페이스에만 DIP
를 적용할 수 있습니다.
public interface IClock { DateTime Now { get; } }
TextWriter
와 같은 추상화 Console
의존성을 완화할 수 있습니다. 종속성 주입은 일반적으로 constructor
주입(소비 클래스의 생성자에 대한 매개변수로 종속성에 추상화 전달) 또는 Setter Injection
setXyz()
{set;}
있는 .Net 속성을 통해 종속성 전달)으로 구현됩니다. 한정된). 생성자 주입은 생성 후 클래스가 올바른 상태에 있도록 보장하고 내부 종속성 필드를 readonly
(C#) 또는 final
(자바)로 표시할 수 있도록 하기 때문에 선호됩니다. 따라서 위의 예에서 생성자 주입을 사용하면 다음과 같이 됩니다.
public class MyLogger : ILogger // Others will depend on our logger. { private readonly TextWriter _output; private readonly IClock _clock; // Dependencies are injected through the constructor public MyLogger(TextWriter stream, IClock clock) { _output = stream; _clock = clock; } public void LogRecord(string somethingToLog) { // We can now use our dependencies through the abstraction // and without knowledge of the lifespans of the dependencies _output.Write("{0:yyyy-MM-dd HH:mm:ss} - {1}", _clock.Now, somethingToLog); } }
DateTime.Now
되돌릴 수 있는 구체적인 Clock
가 제공되어야 하며 두 종속성은 생성자 주입을 통해 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
인스턴스를 반환 ")IDisposable
과 같은 프로토콜을 인식하고 구성된 수명 관리에 따라 종속성 Disposing
일반적으로 IoC 컨테이너가 구성/부트스트랩되면 백그라운드에서 원활하게 작동하므로 코더는 종속성에 대해 걱정하지 않고 현재 코드에 집중할 수 있습니다.
DI 친화적인 코드의 핵심은 클래스의 정적 결합을 피하고 종속성 생성을 위해 new()를 사용하지 않는 것입니다.
위의 예와 같이 종속성을 분리하려면 약간의 설계 노력이 필요하며 개발자에게는 new
하고 종속성을 관리하기 위해 컨테이너를 신뢰하는 습관을 깨는 패러다임 전환이 필요합니다.
그러나 특히 관심 클래스를 철저히 테스트할 수 있다는 점에서 이점이 많습니다.
참고 : POCO/POJO/직렬화 DTO/엔티티 그래프/익명 JSON 프로젝션 등의 생성/매핑/프로젝션( new ..()
통해) 메서드에서 사용되거나 반환된 "데이터 전용" 클래스 또는 레코드는 고려 되지 않습니다. 종속성으로(UML 의미에서) DI의 적용을 받지 않습니다. new
를 사용하여 이를 투영하는 것은 괜찮습니다.
DI(Dependency Injection)의 요점은 애플리케이션 소스 코드를 깨끗 하고 안정적으로 유지하는 것입니다 .
실제로 모든 디자인 패턴은 향후 변경 사항이 최소 파일에 영향을 미치도록 우려를 분리합니다.
DI의 특정 영역은 종속성 구성 및 초기화의 위임입니다.
가끔 Java 외부에서 작업하는 경우 source
가 많은 스크립팅 언어(Shell, Tcl 등 또는 이 목적에 오용되는 Python의 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
종속 하나에 직접 구현 스크립트를 -ing, 당신은 사용 injector.sh
모두 "구성 요소를"래핑 "컨테이너"
#!/bin/sh # Injector source ./archive_files_zip.sh source ./dependent.sh
archive_files
종속성 이 종속 스크립트에 방금 삽입 되었습니다.
tar
또는 xz
사용하여 archive_files
를 구현하는 종속성을 주입했을 수 있습니다.
dependent.sh
스크립트가 종속성을 직접 사용하는 경우 접근 방식을 종속성 조회 (종속성 주입 과 반대)라고 합니다.
#!/bin/sh # Dependent # dependency look-up source ./archive_files_zip.sh touch "one.txt" "two.txt" archive_files "one.txt" "two.txt"
이제 문제는 종속 "구성 요소"가 초기화 자체를 수행해야 한다는 것입니다.
"구성 요소"의 소스 코드는 종속성 초기화의 모든 변경 사항이 "구성 요소"의 소스 코드 파일에 대한 새 릴리스를 요구하기 때문에 깨끗 하지도 안정적이 지도 않습니다.
DI는 Java 프레임워크에서만큼 크게 강조되고 대중화되지 않습니다.
그러나 다음과 같은 문제를 분할하는 일반적인 접근 방식입니다.
종속성 조회 와 함께 구성을 사용하는 것은 구성 매개변수 수가 종속성(예: 새 인증 유형) 및 지원되는 종속성 유형(예: 새 데이터베이스 유형)별로 변경될 수 있으므로 도움이 되지 않습니다.
위의 모든 답변이 좋습니다. 제 목표는 프로그래밍 지식이 없는 사람도 개념을 이해할 수 있도록 개념을 간단하게 설명하는 것입니다.
종속성 주입은 복잡한 시스템을 보다 간단하게 생성하는 데 도움이 되는 디자인 패턴 중 하나입니다.
우리는 일상 생활에서 이 패턴의 다양한 적용을 볼 수 있습니다. 예를 들면 테이프 레코더, VCD, CD 드라이브 등이 있습니다.
위 이미지는 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(); }
당신은 또한 읽을 수 있습니다
예를 들어, 2개의 클래스 Client
와 Service
있습니다. Client
Service
를 사용합니다
public class Service { public void doSomeThingInService() { // ... } }
방법 1)
public class Client { public void doSomeThingInClient() { Service service = new Service(); service.doSomeThingInService(); } }
방법 2)
public class Client { Service service = new Service(); public void doSomeThingInClient() { service.doSomeThingInService(); } }
방법 3)
public class Client { Service service; public Client() { service = new Service(); } public void doSomeThingInClient() { service.doSomeThingInService(); } }
1) 2) 3) 사용
Client client = new Client(); client.doSomeThingInService();
장점
단점
Client
클래스Service
생성자를 변경할 때 Service
객체를 생성하는 모든 위치에서 코드를 변경해야 합니다.방법 1) 생성자 주입
public class Client { Service service; Client(Service service) { this.service = service; } // Example Client has 2 dependency // Client(Service service, IDatabas database) { // this.service = service; // this.database = database; // } public void doSomeThingInClient() { service.doSomeThingInService(); } }
사용
Client client = new Client(new Service()); // Client client = new Client(new Service(), new SqliteDatabase()); client.doSomeThingInClient();
방법 2) 세터 주입
public class Client { Service service; public void setService(Service service) { this.service = service; } public void doSomeThingInClient() { service.doSomeThingInService(); } }
사용
Client client = new Client(); client.setService(new Service()); client.doSomeThingInClient();
방법 3) 인터페이스 주입
https://en.wikipedia.org/wiki/Dependency_injection 확인
===
이제 이 코드는 이미 Dependency Injection
따르고 있으며 테스트 Client
클래스에 더 쉽습니다.
그러나 우리는 여전히 new Service()
Service
생성자를 변경할 때 좋지 않습니다. 이를 방지하기 위해 다음과 같은 DI 인젝터를 사용할 수 있습니다.
1) 간단한 수동 Injector
public class Injector { public static Service provideService(){ return new Service(); } public static IDatabase provideDatatBase(){ return new SqliteDatabase(); } public static ObjectA provideObjectA(){ return new ObjectA(provideService(...)); } }
사용
Service service = Injector.provideService();
2) 라이브러리 사용: Android dagger2의 경우
장점
Service
를 변경할 때 Injector 클래스에서만 변경하면 됩니다.Constructor Injection
을 사용하면 Client
생성자를 보면 Client
클래스의 종속성이 몇 개인지 알 수 있습니다.단점
Constructor Injection
을 사용 Client
가 생성될 때 Service
객체가 생성되는데, 간혹 Service
를 사용하지 않고 Client
클래스의 함수를 사용하기 때문에 생성된 Service
는 낭비된다.https://en.wikipedia.org/wiki/Dependency_injection
종속성은 사용할 수 있는 개체입니다(
Service
).
주입은 종속성(Service
)을 사용하는 종속 개체(Client
)에 전달하는 것입니다.
DI(Dependency Injection)는 서로 종속된 개체를 분리하는 것을 의미합니다. 객체 A가 객체 B에 종속되어 있으므로 아이디어는 이러한 객체를 서로 분리하는 것입니다. 컴파일 시간에도 불구하고 런타임에 개체에 대한 종속성을 공유하는 대신 new 키워드를 사용하여 개체를 하드 코딩할 필요가 없습니다. 에 대해 이야기하자면
new 키워드를 사용하여 객체를 하드 코딩할 필요가 없으며 구성 파일에서 빈 종속성을 정의합니다. 스프링 컨테이너는 모든 연결을 담당합니다.
IOC는 일반적인 개념으로 다양한 방식으로 표현될 수 있으며 종속성 주입이 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는 빈을 인스턴스화하기 위해 인수가 없는 생성자 또는 인수가 없는 정적 팩토리 메서드를 호출한 후 빈에서 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 주석보다 기반한 주석을 사용하면 setter를 필수 종속성으로 만드는 데 사용할 수 있습니다.
내가 생각할 수 있는 가장 좋은 비유는 수술실에서 외과의와 그의 조수입니다. 여기서 외과의는 주인공이고 외과의가 하나에 집중할 수 있도록 필요할 때 다양한 수술 구성 요소를 제공하는 조수입니다. 그가 가장 잘하는 것(수술). 보조자가 없으면 외과의 사는 필요할 때마다 직접 부품을 가져와야 합니다.
DI는 간단히 말해서 종속 구성 요소를 제공하여 구성 요소에 대한 공통 추가 책임(부담)을 제거하는 기술입니다.
surgeon who can concentrate on surgery
있는 외과의사처럼 SR(Single Responsibility) 원칙에 더 가까이 다가갑니다.
DI 사용 시기 : 거의 모든 프로덕션 프로젝트(소형/대형), 특히 끊임없이 변화하는 비즈니스 환경에서 DI를 사용하는 것이 좋습니다. :)
이유 : 변경 사항을 신속하게 테스트하고 시장에 푸시할 수 있도록 코드를 쉽게 테스트 가능하고 조롱할 수 있기를 원하기 때문입니다. 게다가 더 많은 제어 권한이 있는 코드베이스로의 여정을 지원하는 멋진 무료 도구/프레임워크가 많이 있는데도 왜 그렇지 않겠습니까?
이는 개체가 작업을 수행하는 데 필요한 만큼만 종속성을 가져야 하고 종속성이 적어야 함을 의미합니다. 또한 개체의 종속성은 가능하면 "구체적인" 개체가 아니라 인터페이스에 있어야 합니다. (구체적인 객체는 키워드 new로 생성된 모든 객체입니다.) 느슨한 결합은 재사용 가능성을 높이고 유지 관리를 용이하게 하며 값비싼 서비스 대신 "모의" 객체를 쉽게 제공할 수 있도록 합니다.
"Dependency Injection"(DI)은 "Inversion of Control"(IoC)이라고도 하며 이러한 느슨한 결합을 장려하는 기술로 사용할 수 있습니다.
DI 구현에는 두 가지 기본 접근 방식이 있습니다.
생성자에 개체 종속성을 전달하는 기술입니다.
생성자는 구체적인 객체가 아닌 인터페이스를 허용한다는 점에 유의하십시오. 또한 orderDao 매개변수가 null인 경우 예외가 throw됩니다. 이것은 유효한 의존성을 받는 것의 중요성을 강조합니다. 내 생각에 생성자 주입은 객체에 종속성을 부여하는 데 선호되는 메커니즘입니다. 적절한 실행을 위해 "Person" 개체에 종속성을 제공해야 하는 개체를 호출하는 동안 개발자에게 명확합니다.
그러나 다음 예를 고려하십시오. 종속성이 없는 10개의 메서드가 있는 클래스가 있지만 IDAO에 대한 종속성이 있는 새 메서드를 추가한다고 가정합니다. 생성자 주입을 사용하도록 생성자를 변경할 수 있지만 이렇게 하면 모든 곳에서 모든 생성자 호출을 변경해야 할 수 있습니다. 또는 종속성을 취하는 새 생성자를 추가할 수 있지만 개발자는 한 생성자를 다른 생성자보다 사용해야 하는 경우를 쉽게 알 수 있습니다. 마지막으로, 종속성을 생성하는 데 비용이 매우 많이 든다면 드물게만 사용될 수 있는데 왜 생성자에 전달하고 생성자에게 전달해야 합니까? "세터 주입"은 이와 같은 상황에서 사용할 수 있는 또 다른 DI 기술입니다.
Setter 주입은 종속성을 생성자에 강제로 전달하지 않습니다. 대신, 종속성은 필요한 개체에 의해 노출되는 공용 속성에 설정됩니다. 이전에 암시했듯이 이를 수행하는 주요 동기는 다음과 같습니다.
다음은 위의 코드가 어떻게 생겼는지에 대한 예입니다.
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;
이미 많은 답변이 있다는 것을 알고 있지만 이것이 매우 유용하다는 것을 알았습니다. 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
필요한 4개의 값인 4개의 매개변수를 사용합니다. MyDao
클래스는 여전히 이 네 가지 값에 의존하지만 더 이상 이러한 종속성 자체를 만족하지 않습니다. MyDao
인스턴스를 생성하는 모든 클래스에서 제공합니다.
다들 DI용으로 글을 쓰셨으니 몇가지만 여쭤보겠습니다..
이것은 @Adam N이 게시한 답변을 기반으로 합니다.
PersonService가 더 이상 GroupMembershipService에 대해 걱정할 필요가 없는 이유는 무엇입니까? 방금 GroupMembership에는 의존하는 여러 가지(객체/속성)이 있다고 언급했습니다. PService에 GMService가 필요했다면 속성으로 가질 수 있습니다. 주입 여부에 관계없이 조롱할 수 있습니다. 내가 주입되기를 원하는 유일한 시간은 GMService에 런타임까지 알 수 없는 보다 구체적인 하위 클래스가 있는 경우입니다. 그런 다음 하위 클래스를 주입하고 싶을 것입니다. 또는 싱글톤 또는 프로토타입으로 사용하려는 경우. 솔직히 말해서, 구성 파일에는 컴파일 시간 동안 주입할 유형(인터페이스)의 하위 클래스까지 모든 것이 하드코딩되어 있습니다.
편집하다
DI에 대한 Jose Maria Aranz의 멋진 코멘트
DI는 종속성 방향을 결정하고 글루 코드를 작성할 필요를 제거하여 응집력을 높입니다.
거짓. 종속성의 방향은 XML 형식 또는 주석으로 되어 있으며 종속성은 XML 코드 및 주석으로 작성됩니다. XML과 주석은 소스 코드입니다.
DI는 모든 구성 요소를 모듈식(즉, 교체 가능)으로 만들고 서로에 대해 잘 정의된 인터페이스를 가짐으로써 결합을 줄입니다.
거짓. 인터페이스를 기반으로 모듈식 코드를 빌드하기 위해 DI 프레임워크가 필요하지 않습니다.
대체 가능 정보: 매우 간단한 .properties 아카이브 및 Class.forName을 사용하여 변경할 수 있는 클래스를 정의할 수 있습니다. 코드의 클래스를 변경할 수 있는 경우 Java가 적합하지 않은 경우 스크립팅 언어를 사용하십시오. 그런데 주석은 다시 컴파일하지 않고 변경할 수 없습니다.
제 생각에는 DI 프레임워크에 대한 단 하나의 이유가 있습니다. 바로 보일러 플레이트 축소입니다. 잘 만들어진 공장 시스템을 사용하면 선호하는 DI 프레임워크와 동일하고 더 제어되고 예측 가능한 작업을 수행할 수 있습니다. DI 프레임워크는 코드 축소를 약속합니다(XML 및 주석도 소스 코드임). 문제는 이 보일러 플레이트 감소가 매우 간단한 경우(클래스당 하나의 인스턴스 및 이와 유사한 경우)에서 현실적이며 때로는 실제 세계에서 적절한 서비스 객체를 선택하는 것이 클래스를 싱글톤 객체에 매핑하는 것만큼 쉽지 않다는 것입니다.
인기 있는 답변은 유용하지 않은 방식으로 종속성 주입을 정의하기 때문에 도움이 되지 않습니다. "종속성"이란 객체 X가 필요로 하는 기존의 다른 객체를 의미한다는 데 동의합시다. 그러나 우리는 "의존성 주입"을 하고 있다고 말하지 않습니다.
$foo = Foo->new($bar);
매개변수를 생성자에 전달하기만 하면 됩니다. 우리는 생성자가 발명된 이후로 정기적으로 그렇게 해왔습니다.
"종속성 주입"은 일종의 "제어 역전"으로 간주되며, 이는 호출자에서 일부 논리가 제거됨을 의미합니다. 호출자가 매개변수를 전달할 때는 그렇지 않으므로 DI인 경우 DI는 제어 역전을 의미하지 않습니다.
DI는 호출자와 종속성을 관리하는 생성자 사이에 중간 수준이 있음을 의미합니다. Makefile은 종속성 주입의 간단한 예입니다. "호출자"는 명령줄에 "make bar"를 입력하는 사람이고 "생성자"는 컴파일러입니다. Makefile은 bar가 foo에 의존하도록 지정하고,
gcc -c foo.cpp; gcc -c bar.cpp
하기 전에
gcc foo.o bar.o -o bar
"make bar"를 입력하는 사람은 bar가 foo에 의존한다는 것을 알 필요가 없습니다. "make bar"와 gcc 사이에 의존성이 주입되었습니다.
중급 수준의 주요 목적은 종속성을 생성자에 전달하는 것뿐만 아니라 모든 종속성을 한 곳에 나열하고 코더로부터 숨기는 것입니다(코더가 제공하도록 하지 않음).
일반적으로 중간 수준은 요청된 각 개체 유형이 충족해야 하는 역할을 제공해야 하는 구성된 개체에 대한 팩토리를 제공합니다. 그것은 구성의 세부 사항을 숨기는 중간 수준을 가짐으로써 이미 공장에서 부과하는 추상화 페널티를 발생 시켰기 때문에 공장을 사용하는 것이 좋습니다.
종속성 주입 은 코드의 한 부분(예: 클래스)이 하드코딩되지 않은 모듈식 방식으로 종속성(코드의 다른 부분, 예: 다른 클래스, 종속됨)에 액세스할 수 있는 방법(사실상 어떤 방식으로든)을 의미합니다. 필요에 따라 자유롭게 변경하거나 재정의하거나 다른 시간에 로드할 수도 있습니다.)
(그리고 ps, 예, 다소 단순한 개념에 대해 지나치게 과장된 25$ 이름이 되었습니다) , 내 .25
센트
종속성 주입은 일반적으로 "종속성 난독화" 요구 사항이라고 할 수 있는 한 가지 가능한 솔루션입니다. 의존성 난독화는 그것을 요구하는 클래스에 의존성을 제공하는 과정에서 '명백한' 특성을 취함으로써 어떤 식으로든 상기 클래스에 대한 상기 의존성의 제공을 난독화하는 방법입니다. 이것이 반드시 나쁜 것은 아닙니다. 사실, 종속성이 클래스에 제공되는 방식을 난독화함으로써 클래스 외부의 무언가가 종속성을 생성할 책임이 있습니다. 즉, 다양한 시나리오에서 종속성의 다른 구현이 변경 없이 클래스에 제공될 수 있음을 의미합니다. 수업에. 이것은 프로덕션 모드와 테스트 모드 사이를 전환하는 데 유용합니다(예: '모의' 서비스 종속성 사용).
불행히도 나쁜 부분은 일부 사람들이 종속성 난독화를 수행하기 위해 특수 프레임워크가 필요하고 특정 프레임워크를 사용하지 않기로 선택하면 어떻게든 '덜' 프로그래머라고 가정했다는 것입니다. 많은 사람들이 믿고 있는 또 다른 극도로 혼란스러운 신화는 종속성 주입이 종속성 난독화를 달성하는 유일한 방법이라는 것입니다. 이것은 명백하고 역사적으로 명백히 100% 잘못되었지만 의존성 난독화 요구사항에 대한 의존성 주입에 대한 대안이 있다는 것을 일부 사람들에게 확신시키는 데 어려움을 겪을 것입니다.
프로그래머는 수년 동안 종속성 난독화 요구 사항을 이해했으며 종속성 주입이 생각되기 전후에 많은 대안 솔루션이 발전했습니다. 팩토리 패턴이 있지만 특정 인스턴스에 대한 주입이 필요하지 않은 ThreadLocal을 사용하는 많은 옵션이 있습니다. 종속성은 스레드에 효과적으로 주입되어 객체를 (편리한 정적 getter 메서드를 통해) 사용 가능한 모든 클래스에서 사용할 수 있도록 합니다. 이를 필요로 하는 클래스에 주석을 추가하고 복잡한 XML '접착제'를 설정하지 않고도 이를 필요로 합니다. 지속성을 위해 종속성이 필요한 경우(JPA/JDO 또는 기타) 'tranaparent persistence'를 훨씬 쉽게 달성할 수 있으며 도메인 모델 및 비즈니스 모델 클래스가 순전히 POJO(즉, 프레임워크 특정/주석에 잠겨 있지 않음)로 구성된 비즈니스 모델 클래스를 사용할 수 있습니다.
책에서 ' 잘 기초된 Java 개발자: Java 7 및 다국어 프로그래밍의 핵심 기술
DI는 종속성을 찾는 프로세스가 현재 실행 중인 코드의 직접적인 제어를 벗어나는 IoC의 특정 형태입니다.
5세 아동을 위한 의존성 주입.
냉장고에서 직접 물건을 꺼낼 때 문제가 발생할 수 있습니다. 문을 열어두거나 엄마나 아빠가 원하지 않는 것을 얻게 될 수도 있습니다. 우리가 갖고 있지 않거나 만료된 것을 찾고 있을 수도 있습니다.
당신이 해야 할 일은 "점심과 함께 마실 것이 필요합니다."라고 요구 사항을 말하는 것입니다. 그러면 우리는 당신이 앉을 때 먹을 것이 있는지 확인합니다.
책 Apress.Spring.Persistence.with.Hibernate.Oct.2010에서
종속성 주입의 목적은 외부 소프트웨어 구성 요소를 해결하는 작업을 애플리케이션 비즈니스 논리에서 분리하는 것입니다. 종속성 주입이 없으면 구성 요소가 필요한 서비스에 액세스하는 방법에 대한 세부 정보가 구성 요소의 코드와 혼동될 수 있습니다. 이는 오류 가능성을 증가시킬 뿐만 아니라 코드 팽창을 추가하고 유지 관리 복잡성을 확대합니다. 구성 요소를 더 밀접하게 결합하여 리팩토링 또는 테스트할 때 종속성을 수정하기 어렵게 만듭니다.
DI(Dependency Injection)는 IoC(Inversion of Control)라고도 하는 DIP(Dependency Inversion Principle) 방식의 일부입니다. 기본적으로 하나의 모놀리식 시스템 대신 코드를 더 모듈화하고 단위 테스트 가능하게 만들고 싶기 때문에 DIP를 수행해야 합니다. 따라서 클래스에서 분리되고 추상화될 수 있는 코드 부분을 식별하기 시작합니다. 이제 추상화 구현을 클래스 외부에서 주입해야 합니다. 일반적으로 이것은 생성자를 통해 수행할 수 있습니다. 따라서 추상화를 매개변수로 받아들이는 생성자를 생성하고 이를 종속성 주입(생성자를 통해)이라고 합니다. DIP, DI, 당신이 읽을 수있는 IoC 컨테이너에 대한 자세한 설명은 여기에
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가 IoC(Inversion of Control)와 밀접하게 결합된 이유입니다. 즉, 우리 프로그램은 코드에 빈을 주입하는 다른 실행 중인 모듈을 제어하도록 전달합니다. (주입될 수 있는 각 객체는 서명되거나 Bean으로 간주될 수 있습니다.) 예를 들어 Spring에서는 이 작업을 수행하는 ApplicationContext 컨테이너를 생성하고 초기화하여 수행됩니다. 코드에서 컨텍스트를 생성하고 초기화 빈을 호출하기만 하면 됩니다. 그 순간 주입이 자동으로 수행되었습니다.
Christoffer Noring, Pablo Deeleman의 책 "Learning Angular - Second Edition"에서:
" 응용 프로그램이 성장하고 발전함에 따라 각 코드 엔터티 는 소프트웨어 엔지니어링 세계에서 종속성 으로 더 잘 알려진 다른 개체의 인스턴스를 내부적으로 필요로 합니다. 이러한 종속성 을 종속 클라이언트에 전달 하는 작업 을 주입이라고 합니다 . 그리고 그것은 또한 인젝터 라는 다른 코드 엔티티의 참여를 수반합니다 인젝터 는 클라이언트에 성공적으로 주입되는 순간부터 사용할 준비가 되도록 필요한 종속성 을 인스턴스화 하고 부트스트랩 하는 책임을 집니다. 이것은 매우 중요합니다. 클라이언트는 자신의 종속성 을 인스턴스화 하는 방법에 대해 아무 것도 모르고 사용하기 위해 구현 하는 인터페이스 만 알고 있습니다."
보낸 사람: Anton Moiseev. 책 "Typescript를 사용한 Angular Development, Second Edition.":
"요컨대, DI 는 느슨하게 결합된 방식으로 코드를 작성하는 데 도움이 되며 코드를 보다 테스트 가능 하고 재사용 가능하게 만듭니다."
간단히 말해서 종속성 주입(DI)은 종속성을 제거하거나 서로 다른 개체 간의 긴밀한 결합을 제거하는 방법입니다. 종속성 주입은 각 개체에 응집력 있는 동작을 제공합니다.
DI는 "전화하지 마십시오"라고 말하는 Spring의 IOC 교장을 구현한 것입니다. 의존성 주입 프로그래머를 사용하면 new 키워드를 사용하여 객체를 생성할 필요가 없습니다.
객체는 Spring 컨테이너에 한 번 로드된 다음 getBean(String beanName) 메소드를 사용하여 Spring 컨테이너에서 해당 객체를 가져와 필요할 때마다 재사용합니다.
출처 : http:www.stackoverflow.com/questions/130794/what-is-dependency-injection
정의되지 않은 객체 속성 감지 (0) | 2021.10.26 |
---|---|
SQL Server의 기존 테이블에 기본값이 있는 열 추가 (0) | 2021.10.26 |
JavaScript에서 문자열을 부울로 변환하려면 어떻게 해야 합니까? (0) | 2021.10.26 |
문자열의 단어를 어떻게 반복합니까? (0) | 2021.10.26 |
JavaScript에서 여러 줄 문자열 만들기 (0) | 2021.10.26 |