Dependency Injection이란?
의존성
: A클래스가 B클래스에 대한 참조가 있을 경우 A는 B에 의존한다라고 하고 B는 A에 대한 종속성이 있다(종속한다)라고 합니다.
이 의존성을 잘 관리하지 않으면, 프로젝트의 규모가 커질수록 코드변화에 유연하게 대처할 수 없어서 많은 코드 수정을 하게 될 수도 있습니다.
SOLID DIP
(Dependency Inversion Principle)
A. HIGH LEVEL MODULES SHOULD NOT DEPEND UPON LOW LEVEL MODULES. BOTH SHOULD DEPEND UPON ABSTRACTIONS.
B. ABSTRACTIONS SHOULD NOT DEPEND UPON DETAILS. DETAILS SHOULD DEPEND UPON ABSTRACTIONS.
DIP
를 직역하면,
상위 모듈은 하위 모듈에 의존하면 안된다. 둘다 추상화에 의존해야 한다.
추상적인 것은 구체적인 것에 의존해서는 안된다. 구체적인 것은 추상적인 것을 의존해야 한다.
즉, DIP
를 만족하게 하려면 구체적인 클래스보다는 인터페이스나 추상클래스와 의존관계를 맺도록 설계해야 한다는 것입니다.
이 DIP를 만족하면 의존성 주입을 통해서 보다 변화를 쉽게 수용할 수 있는 코드를 작성할 수 있게 됩니다.
의존성 주입을 사용하지 않은 코드
class Computer {
private var cpu: CPU? = M1Cpu()
private var ram: RAM? = Ram8G()
}
interface CPU
class M1Cpu: CPU {}
class I7Cpu: CPU {}
interface RAM
class Ram16G: RAM {}
class Ram8G: RAM {}
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val computer = Computer()
}
}
위 코드는 Computer 클래스 내부에서 Cpu의 인스턴스와 Ram의 인스턴스를 직접 생성하였습니다.
- Computer 객체는 Cpu와 Ram 객체의 생성을 직접 제어하기 때문에 두 객체간에 강한 결합(Tight Coupling)이 생기고, Cpu와 Ram을 다른 객체로 바꿔주는 방법은 Computer 클래스를 수정하거나, 새로운 Computer 클래스를 생성해서 I7Cpu를 넣어 주는 방법 밖에 없습니다.
- 이러한 설계 방법으로 인해, 의존 클래스가 변경된다면 종속 클래스 또한 변경 될 수 밖에 없습니다.
- 높은 결합도로 인해서 Unit Test를 어렵게 합니다.
의존성 주입
: 클래스가자체 의존성을 만들지 않고, 다른 곳에서 인스턴스화 한 다음 이를 필요로 하는 클래스에 전달하는 것.
class Computer {
private var cpu: CPU? = null
private var ram: RAM? = null
constructor(cpu: CPU, ram: RAM) {
this.cpu = cpu
this.ram = ram
}
}
interface CPU
class M1Cpu: CPU {}
class I7Cpu: CPU {}
interface RAM
class Ram16G: RAM {}
class Ram8G: RAM {}
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val ram = Ram8G()
val cpu = M1Cpu()
val computer = Computer(cpu, ram)
}
}
제어의 역전(Inversion of Control)
: 제어의 역전이란 어떤 일의 수행하도록 만들어진 프레임워크에 제어권을 위임함으로써 관심사를 분리하는 것.
현재 이 코드에서는 생성자 주입을 통해서 Computer 클래스에서 CPU와 RAM을 만들지 않고 코드의 생성을 외부에 위임했습니다.
→ CPU나 RAM을 변경해야 한다고 해서 Computer클래스를 변경할 필요가 없습니다. (의존성을 주입하는 쪽에서 설정만 바꿔주면 된다.)
의존성 주입의 장점
- 테스트 하기 쉬워진다. (의존성 주입을 사용하는 결과로 mock 객체나 stub을 사용하여 단위 테스트를 하기 쉬워집니다. )
- 결합도를 낮춰준다. (재사용성, 확장성 ↑)
- 인터페이스 기반으로 설계하여 코드를 유연하게 만들어준다.